YYKit 源码讲解(2)

接下来我们看base文件下的Foundation文件夹下文件

Base - Foundation

这个文件夹里面的文件都是给foundation里面的部分类添加的category

1.NSObject+YYAdd

分四个部分

1.sending messages with variable parameters

2.Swap method (Swizzling)

3.Associate value

4. Others

sending messages with variable parameters

先看宏定义

#define INIT_INV(_last_arg_, _return_) \

NSMethodSignature * sig = [self methodSignatureForSelector:sel]; \

if (!sig) { [self doesNotRecognizeSelector:sel]; return _return_; } \

NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig]; \

if (!inv) { [self doesNotRecognizeSelector:sel]; return _return_; } \

[inv setTarget:self]; \

[inv setSelector:sel]; \

va_list args; \

va_start(args, _last_arg_); \

[NSObject setInv:inv withSig:sig andArgs:args]; \

va_end(args);

这个宏定义就是 创建一个NSInvocation 

不过这里面调用一个自定义函数+ (void)setInv:(NSInvocation *)inv withSig:(NSMethodSignature *)sig andArgs:(va_list)args

看懂这个函数就了解下面的c函数的用法

 va_start、va_arg、va_end

我认为哈va_start 将 ...参数列表赋值到 va_list 中

va_arg 调用一次这个函数就相当于偏移了多少个字节

va_end 结束。

上图


所以这个函数的主要的作用就是通过参数获取NSMethodSignature 参数列表对应中参数对应的字节数就行了,就能从va_list 中获取到参数。

char *type = (char *)[sig getArgumentTypeAtIndex:index];

while (*type == 'r' || // const

*type == 'n' || // in

*type == 'N' || // inout

*type == 'o' || // out

*type == 'O' || // bycopy

*type == 'R' || // byref

*type == 'V') { // oneway

type++; // cutoff useless prefix

}


这里是排除掉那些参数不是基本类型的。

下面这个写法是ReactiveCocoa 提供的方法 。两个方法可以互相借鉴下。

- (id)aspect_argumentAtIndex:(NSUInteger)index {

const char *argType = [self.methodSignature getArgumentTypeAtIndex:index];

// Skip const type qualifier.

if (argType[0] == _C_CONST) argType++;

#define WRAP_AND_RETURN(type) do { type val = 0; [self getArgument:&val atIndex:(NSInteger)index]; return @(val); } while (0)

if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) {

__autoreleasing id returnObj;

[self getArgument:&returnObj atIndex:(NSInteger)index];

return returnObj;

} else if (strcmp(argType, @encode(SEL)) == 0) {

SEL selector = 0;

[self getArgument:&selector atIndex:(NSInteger)index];

return NSStringFromSelector(selector);

} else if (strcmp(argType, @encode(Class)) == 0) {

__autoreleasing Class theClass = Nil;

[self getArgument:&theClass atIndex:(NSInteger)index];

return theClass;

// Using this list will box the number with the appropriate constructor, instead of the generic NSValue.

} else if (strcmp(argType, @encode(char)) == 0) {

WRAP_AND_RETURN(char);

} else if (strcmp(argType, @encode(int)) == 0) {

WRAP_AND_RETURN(int);

} else if (strcmp(argType, @encode(short)) == 0) {

WRAP_AND_RETURN(short);

} else if (strcmp(argType, @encode(long)) == 0) {

WRAP_AND_RETURN(long);

} else if (strcmp(argType, @encode(long long)) == 0) {

WRAP_AND_RETURN(long long);

} else if (strcmp(argType, @encode(unsigned char)) == 0) {

WRAP_AND_RETURN(unsigned char);

} else if (strcmp(argType, @encode(unsigned int)) == 0) {

WRAP_AND_RETURN(unsigned int);

} else if (strcmp(argType, @encode(unsigned short)) == 0) {

WRAP_AND_RETURN(unsigned short);

} else if (strcmp(argType, @encode(unsigned long)) == 0) {

WRAP_AND_RETURN(unsigned long);

} else if (strcmp(argType, @encode(unsigned long long)) == 0) {

WRAP_AND_RETURN(unsigned long long);

} else if (strcmp(argType, @encode(float)) == 0) {

WRAP_AND_RETURN(float);

} else if (strcmp(argType, @encode(double)) == 0) {

WRAP_AND_RETURN(double);

} else if (strcmp(argType, @encode(BOOL)) == 0) {

WRAP_AND_RETURN(BOOL);

} else if (strcmp(argType, @encode(bool)) == 0) {

WRAP_AND_RETURN(BOOL);

} else if (strcmp(argType, @encode(char *)) == 0) {

WRAP_AND_RETURN(const char *);

} else if (strcmp(argType, @encode(void (^)(void))) == 0) {

__unsafe_unretained id block = nil;

[self getArgument:&block atIndex:(NSInteger)index];

return [block copy];

} else {

NSUInteger valueSize = 0;

NSGetSizeAndAlignment(argType, &valueSize, NULL);

unsigned char valueBytes[valueSize];

[self getArgument:valueBytes atIndex:(NSInteger)index];

return [NSValue valueWithBytes:valueBytes objCType:argType];

}

return nil;

#undef WRAP_AND_RETURN

}

+ (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel

交换实例的两个方法实现

+ (BOOL)swizzleClassMethod:(SEL)originalSel with:(SEL)newSel

交换类的两个方法实现

- (void)setAssociateValue:(id)value withKey:(void *)key

关联引用 强引用

-(void)setAssociateWeakValue:(id)value withKey:(void *)key

关联引用若引用

- (void)removeAssociatedValues

移除所有的关联引用

- (id)getAssociatedValueForKey:(void *)key 

获取关联引用某个key的值

+ (NSString *)className

获取类名

- (NSString *)className

获取类名


2.NSObject+YYAddForARC

现在都用arc 了。不做分析,要是想使用这个类,要添加参数-fno-objc-arc

3.NSObject+YYAddForKVO

这个类有三个public方法

1.- (void)addObserverBlockForKeyPath:(NSString*)keyPath block:(void (^)(id _Nonnull obj, _Nullable id oldVal, _Nullable id newVal))block; 

2.- (void)removeObserverBlocksForKeyPath:(NSString*)keyPath;

3.- (void)removeObserverBlocks;

这其实把回调封装到一个block 里面了。书写简单了

- (void)addObserverBlockForKeyPath:(NSString *)keyPath block:(void (^)(__weak id obj, id oldVal, id newVal))block {

if (!keyPath || !block) return;

_YYNSObjectKVOBlockTarget *target = [[_YYNSObjectKVOBlockTarget alloc] initWithBlock:block];

NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks];

NSMutableArray *arr = dic[keyPath];

if (!arr) {

arr = [NSMutableArray new];

dic[keyPath] = arr;

}

[arr addObject:target];

[self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];

}

第一步:这个函数 先生成一个_YYNSObjectKVOBlockTarget 对象

第二步:调用_yy_allNSObjectObserverBlocks 放获取一个dic,设置值keypath 为key value值是arr arr 中保存 _YYNSObjectKVOBlockTarget对象

第三步:让keypath 的观察者设置为 _YYNSObjectKVOBlockTarget (这样,所有keypath有变动都会回调到_YYNSObjectKVOBlockTarget 中)

_YYNSObjectKVOBlockTarget 这个类

- (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block {

self = [super init];

if (self) {

self.block = block;

}

return self;

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

if (!self.block) return;

BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];

if (isPrior) return;

NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];

if (changeKind != NSKeyValueChangeSetting) return;

id oldVal = [change objectForKey:NSKeyValueChangeOldKey];

if (oldVal == [NSNull null]) oldVal = nil;

id newVal = [change objectForKey:NSKeyValueChangeNewKey];

if (newVal == [NSNull null]) newVal = nil;

self.block(object, oldVal, newVal);

}

比较简单就是保存一个回调block  。再就是增加一个观察者回调。

- (NSMutableDictionary *)_yy_allNSObjectObserverBlocks {

NSMutableDictionary *targets = objc_getAssociatedObject(self, &block_key);

if (!targets) {

targets = [NSMutableDictionary new];

objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

return targets;

}

获取一个dic 没有就生成一个dic 绑定到self上

这里注意 block 中的obj 是__weak修饰的弱引用。

这里主要要看的是这个观察者的回调方法。

/* Given that the receiver has been registered as an observer of the value at a key path relative to an object, be notified of a change to that value.

给观察者发通知

The change dictionary always contains an NSKeyValueChangeKindKey entry whose value is an NSNumber wrapping an NSKeyValueChange (use -[NSNumber unsignedIntegerValue]). The meaning of NSKeyValueChange depends on what sort of property is identified by the key path:

字典中始终包含NSKeyValueChangeKindKey 对应的值是NSNumber (可以转化成NSKeyValueChange ) 

- For any sort of property (attribute, to-one relationship, or ordered or unordered to-many relationship) NSKeyValueChangeSetting indicates that the observed object has received a -setValue:forKey: message, or that the key-value coding-compliant set method for the key has been invoked, or that a -willChangeValueForKey:/-didChangeValueForKey: pair has otherwise been invoked.

调用setValue:forKey: 方法NSKeyValueChange 的值是NSKeyValueChangeSetting 

- For an _ordered_ to-many relationship, NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, and NSKeyValueChangeReplacement indicate that a mutating message has been sent to the array returned by a -mutableArrayValueForKey: message sent to the object, or sent to the ordered set returned by a -mutableOrderedSetValueForKey: message sent to the object, or that one of the key-value coding-compliant array or ordered set mutation methods for the key has been invoked, or that a -willChange:valuesAtIndexes:forKey:/-didChange:valuesAtIndexes:forKey: pair has otherwise been invoked.

- For an _unordered_ to-many relationship (introduced in Mac OS 10.4), NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, and NSKeyValueChangeReplacement indicate that a mutating message has been sent to the set returned by a -mutableSetValueForKey: message sent to the object, or that one of the key-value coding-compliant set mutation methods for the key has been invoked, or that a -willChangeValueForKey:withSetMutation:usingObjects:/-didChangeValueForKey:withSetMutation:usingObjects: pair has otherwise been invoked.

For any sort of property, the change dictionary contains an NSKeyValueChangeNewKey entry if NSKeyValueObservingOptionNew was specified at observer registration time, it's the right kind of change, and this isn't a prior notification. The change dictionary contains an NSKeyValueChangeOldKey if NSKeyValueObservingOptionOld was specified and it's the right kind of change. See the comments for the NSKeyValueObserverNotification informal protocol methods for what the values of those entries can be.

For an _ordered_ to-many relationship, the change dictionary always contains an NSKeyValueChangeIndexesKey entry whose value is an NSIndexSet containing the indexes of the inserted, removed, or replaced objects, unless the change is an NSKeyValueChangeSetting.

If NSKeyValueObservingOptionPrior (introduced in Mac OS 10.5) was specified at observer registration time, and this notification is one being sent prior to a change as a result, the change dictionary contains an NSKeyValueChangeNotificationIsPriorKey entry whose value is an NSNumber wrapping YES (use -[NSNumber boolValue]).

context is always the same pointer that was passed in at observer registration time.

*/


好长的介绍。

这里有篇介绍kvo的。可以一看.这个文章更详细。

1通知分为自动通知和 手动通知

2 手动通知和自动通知都可以一对一,或者一对多通知

3 一对多通知的手动通知针对数组 要使用mutableArrayValueForKey方法 

4.通知之间可以添加依赖关系。


4.NSString+YYAdd

1这个类第一部分是加密算法 md2  md4 md5 sha1 sha224 sha256 sha384 sha512 crc32 hmacMD5 hmacsha1 hmacsha224 hmacsha256 hmacsha384 hmacsha512  真正的实现在NSData+YYAdd 这里不做介绍

2 接下来是两个base64 加密, 分实例方法和 类方法。 依赖NSData+YYAdd 在该类分析

- (NSString *)base64EncodedString

+ (NSString *)stringWithBase64EncodedString:(NSString *)base64EncodedString 


3 url encode decode 这里我也不是很懂编解码。就这样用吧。

- (NSString *)stringByURLEncode

- (NSString *)stringByURLDecode

4. 字符转换成html 

- (NSString *)stringByEscapingHTML {

NSUInteger len = self.length;

if (!len) return self;

unichar *buf = malloc(sizeof(unichar) * len);

if (!buf) return self;

[self getCharacters:buf range:NSMakeRange(0, len)];

NSMutableString *result = [NSMutableString string];

for (int i = 0; i < len; i++) {

unichar c = buf[i];

NSString *esc = nil;

switch (c) {

case 34: esc = @"""; break;

case 38: esc = @"&"; break;

case 39: esc = @"'"; break;

case 60: esc = @"<"; break;

case 62: esc = @">"; break;

default: break;

}

if (esc) {

[result appendString:esc];

} else {

CFStringAppendCharacters((CFMutableStringRef)result, &c, 1);

}

}

free(buf);

return result;

}

其实就是字符替换成html 识别的字符而已。

5 计算字符大小

- (CGSize)sizeForFont:(UIFont *)font size:(CGSize)size mode:(NSLineBreakMode)lineBreakMode这个方法兼容了老版本。7.0 以前的版本

- (CGFloat)widthForFont:(UIFont *)font

- (CGFloat)heightForFont:(UIFont *)font width:(CGFloat)width

6 字符串的正则匹配

<1>- (BOOL)matchesRegex:(NSString *)regex options:(NSRegularExpressionOptions)options

<2>- (void)enumerateRegexMatches:(NSString *)regex

options:(NSRegularExpressionOptions)options

usingBlock:(void (^)(NSString *match, NSRange matchRange, BOOL *stop))block

<3>- (NSString *)stringByReplacingRegex:(NSString *)regex

options:(NSRegularExpressionOptions)options

withString:(NSString *)replacement;

这里其实主要正则表达式。可以参考这篇文章学习

第一个是检查是否匹配到正则表达式

第二个是讲匹配到的结果回传回去

第三个是替代

7 延伸一些NSString 和 NSNumber 转换的方法

- (char)charValue

- (unsigned char) unsignedCharValue

- (short) shortValue

- (unsigned short) unsignedShortValue

- (unsigned int) unsignedIntValue

- (long) longValue

- (unsigned long) unsignedLongValue

- (unsigned long long) unsignedLongLongValue

- (NSUInteger) unsignedIntegerValue


8 获取uuid 字符串

+ (NSString *)stringWithUUID

9+ (NSString *)stringWithUTF32Char:(UTF32Char)char32

+ (NSString *)stringWithUTF32Chars:(const UTF32Char *)char32 length:(NSUInteger)length

- (void)enumerateUTF32CharInRange:(NSRange)range usingBlock:(void (^)(UTF32Char char32, NSRange range, BOOL *stop))block

四字节对齐 的字符串

9- (NSString *)stringByTrim 

去掉头尾空格

10 给图片添加scale

- (NSString *)stringByAppendingPathScale:(CGFloat)scale

- (CGFloat)pathScale

11 是否有空格 特定字符串字节

- (BOOL)isNotBlank

- (BOOL)containsString:(NSString *)string

- (BOOL)containsCharacterSet:(NSCharacterSet *)set


12 字符串转换成NSNumber

- (NSNumber *)numberValue

13 - (NSData *)dataValue 

转换成NSData

14 获取字符串长度

- (NSRange)rangeOfAll

15 json decoded 

- (id)jsonValueDecoded 

16 获取内容

+ (NSString *)stringNamed:(NSString *)name

看到这里其实都是写基本用法的封装,不做详细介绍。比较简单

5.NSNumber+YYAdd

+ (NSNumber *)numberWithString:(NSString *)string{ NSString *str = [[string stringByTrim] lowercaseString]; if (!str || !str.length) { return nil; } static NSDictionary *dic; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dic = @{@"true" : @(YES), @"yes" : @(YES), @"false" : @(NO), @"no" : @(NO), @"nil" : [NSNull null], @"null" : [NSNull null], @"" : [NSNull null]};

});

id num = dic[str];

if (num) {

if (num == [NSNull null]) return nil;

return num;

}

// hex number

int sign = 0;

if ([str hasPrefix:@"0x"]) sign = 1;

else if ([str hasPrefix:@"-0x"]) sign = -1;

if (sign != 0) {

NSScanner *scan = [NSScanner scannerWithString:str];

unsigned num = -1;

BOOL suc = [scan scanHexInt:&num];

if (suc)

return [NSNumber numberWithLong:((long)num * sign)];

else

return nil;

}

// normal number

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];

[formatter setNumberStyle:NSNumberFormatterDecimalStyle];

return [formatter numberFromString:string];

}

1 将字符串去掉空格,并且变成小写

2 判断字典中是否包含字符串key,包含key所对应的值

3.检查是不是0x 或者 -0x 开头的字符串 是的换将 0x开头的字符串转换成10进制的

4 正常字符转换成数字。

6.NSData+YYAdd

- (NSString *)md2String

- (NSData *)md2Data

- (NSString *)md4String

- (NSData *)md4Data

- (NSString *)md5String

- (NSData *)md5Data

- (NSString *)sha1String

- (NSData *)sha1Data

- (NSString *)sha224String

- (NSData *)sha224Data

- (NSString *)sha256String

- (NSData *)sha256Data

- (NSString *)sha384String

- (NSData *)sha384Data

- (NSString *)sha512String

- (NSData *)sha512Data

- (NSString *)hmacStringUsingAlg:(CCHmacAlgorithm)alg withKey:(NSString *)key

hmac 所有加密算法的封装

- (NSData *)hmacDataUsingAlg:(CCHmacAlgorithm)alg withKey:(NSData *)key

- (NSString *)hmacMD5StringWithKey:(NSString *)key

- (NSData *)hmacMD5DataWithKey:(NSData *)key

- (NSString *)hmacSHA1StringWithKey:(NSString *)key

- (NSData *)hmacSHA1DataWithKey:(NSData *)key

- (NSString *)hmacSHA224StringWithKey:(NSString *)key

- (NSData *)hmacSHA224DataWithKey:(NSData *)key

- (NSString *)hmacSHA256StringWithKey:(NSString *)key

- (NSData *)hmacSHA256DataWithKey:(NSData *)key

- (NSString *)hmacSHA384StringWithKey:(NSString *)key

- (NSData *)hmacSHA384DataWithKey:(NSData *)key

- (NSString *)hmacSHA512StringWithKey:(NSString *)key

- (NSData *)hmacSHA512DataWithKey:(NSData *)key

- (NSString *)crc32String

- (uint32_t)crc32

- (NSData *)aes256EncryptWithKey:(NSData *)key iv:(NSData *)iv

- (NSData *)aes256DecryptWithkey:(NSData *)key iv:(NSData *)iv

加密算法大汇总。不做介绍,这里要提到大文件加密。大文件加密不能用上面的方式加密,要将文件拆分开分别循环加密才行,详细看YYFIleHash

- (NSString *)utf8String 转换成utf8

- (NSString *)hexString  转换成hex表示

+ (NSData *)dataWithHexString:(NSString *)hexStr hex 转换成data

base64EncodingTable[64]

base64DecodingTable[256] 

两张表 

- (NSString *)base64EncodedString base64 编码

+ (NSData *)dataWithBase64EncodedString:(NSString *)base64EncodedString 解码

- (id)jsonValueDecoded  data 转换成json

- (NSData *)gzipInflate 压缩

- (NSData *)gzipDeflate 解压缩

- (NSData *)zlibInflate 

- (NSData *)zlibDeflate

7.NSArray+YYAdd

+ (NSArray *)arrayWithPlistData:(NSData *)plist

+ (NSArray *)arrayWithPlistString:(NSString *)plist 

读取plist 

- (NSData *)plistData

数组转换成plist

- (NSString *)plistString

转换成字符串

- (id)randomObject

随机获取一个对象

- (id)objectOrNilAtIndex:(NSUInteger)index

获取指定位置的一个对象,没有就是nil

- (NSString *)jsonStringEncoded

数组转json

- (NSString *)jsonPrettyStringEncoded

数组转json  数据格式化,容易阅读

针对NSMutableArray

+ (NSMutableArray *)arrayWithPlistData:(NSData *)plist

+ (NSMutableArray *)arrayWithPlistString:(NSString *)plist

- (void)removeFirstObject 删除第一个元素

- (void)removeLastObject 删除最后一个元素

- (id)popFirstObject 删除第一个元素,并且返回这个元素

- (id)popLastObject 删除最后一个元素,并且返回这个元素

- (void)appendObject:(id)anObject 追加一个元素

- (void)prependObject:(id)anObject 在第一位插入一个元素

- (void)appendObjects:(NSArray *)objects 将数组元素添加到最后面

- (void)prependObjects:(NSArray *)objects 在0 位置插入 数组中元素

- (void)insertObjects:(NSArray *)objects atIndex:(NSUInteger)index 在特点位置插入元素

- (void)reverse     首位交换 

floor 函数 就是舍弃小数点。

- (void)shuffle 随机交换元素  


8.NSDictionary+YYAdd

+ (NSDictionary *)dictionaryWithPlistData:(NSData *)plist

+ (NSDictionary *)dictionaryWithPlistString:(NSString *)plist

读取plist

- (NSData *)plistData

- (NSString *)plistString

字典转换成plist 

- (NSArray *)allKeysSorted  key 排序返回

key 数组中 的key 按照caseInsensitiveCompare: 方式排序

- (NSArray *)allValuesSortedByKeys

value 按照key 排序顺序返回

- (BOOL)containsObjectForKey:(id)key 是否包含key

- (NSDictionary *)entriesForKeys:(NSArray *)keys

获取keys 的组合的dic

- (NSString *)jsonStringEncoded

字典转json

- (NSString *)jsonPrettyStringEncoded

字典转格式化json

+ (NSDictionary *)dictionaryWithXML:(id)xml

xml 转换成 dic 

+ (NSDictionary *)dictionaryWithXML:(id)xml {

_YYXMLDictionaryParser *parser = nil;

if ([xml isKindOfClass:[NSString class]]) {

parser = [[_YYXMLDictionaryParser alloc] initWithString:xml];

} else if ([xml isKindOfClass:[NSData class]]) {

parser = [[_YYXMLDictionaryParser alloc] initWithData:xml];

}

return [parser result];

}

这里有yykit 大神写的转换方法。我们分析一下写法。

其实主要是这三个代理方式是干嘛的

- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict

- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName

- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string

最后一个函数是处理cdata数据的

- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock

1 第一个函数,读取xml文档,发现一个开始节点就调用

2 第二个函数是读取发现任意下一个开始节点或者结束节点之间的内容

3 发现一个结束节点就调用。

采用压栈方式进行xml解析的

这个xml 解析始终有个字段“_name” 属性。看着不爽

所以自已试写了一个。貌似解决问题了。但是不知道有没有其他问题,没验证。简单测试没问题。有兴趣的朋友可以帮着测试测试哈。请指正

我自己试着用野路子写了个xml解析

- (void)textEnd {

_text = _text.stringByTrim.mutableCopy;

if (_text.length) {

NSMutableDictionary *top = _stack.lastObject;

[top[@"super"] setObject:_text forKey:top[@"superName"]];

}

_text = nil;

}

- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict {

NSMutableDictionary *node1 = [NSMutableDictionary new];

if (attributeDict.count) [node1 addEntriesFromDictionary:attributeDict];

if (_root) {

NSMutableDictionary *top = _stack.lastObject;

id  subelement=  [top objectForKey:elementName];

if (subelement) {

if ([subelement isKindOfClass:[NSArray class]]) {

[subelement addObject:node1];

}else{

[top setObject:[@[subelement, node1]mutableCopy] forKey:elementName];

}

}else{

[top setObject:node1 forKey:elementName];

}

[node1 setObject:top forKey:@"super"];

[node1 setObject:elementName forKey:@"superName"];

[_stack addObject:node1];

}else{

_root = [NSMutableDictionary dictionary];

[_root setObject:node1 forKey:elementName];

_stack = [NSMutableArray arrayWithObject:node1];

}

}

- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName {

[self textEnd];

///保存最后一个对象

NSMutableDictionary *top = _stack.lastObject;

[_stack removeLastObject];

[top removeObjectForKey:@"super"];

[top removeObjectForKey:@"superName"];

}

- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string {

if (_text) [_text appendString:string];

else _text = [NSMutableString stringWithString:string];

}

- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {

NSString *string = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];

if (_text) [_text appendString:string];

else _text = [NSMutableString stringWithString:string];

}


static NSNumber *NSNumberFromID(id value)

获取一个nsnumber  

- (BOOL)boolValueForKey:(NSString *)key default:(BOOL)def

- (char)charValueForKey:(NSString *)key default:(char)def

- (unsigned char)unsignedCharValueForKey:(NSString *)key default:(unsigned char)def

- (short)shortValueForKey:(NSString *)key default:(short)def

- (unsigned short)unsignedShortValueForKey:(NSString *)key default:(unsigned short)def

- (int)intValueForKey:(NSString *)key default:(int)def

- (unsigned int)unsignedIntValueForKey:(NSString *)key default:(unsigned int)def

- (long)longValueForKey:(NSString *)key default:(long)def

- (unsigned long)unsignedLongValueForKey:(NSString *)key default:(unsigned long)def

- (long long)longLongValueForKey:(NSString *)key default:(long long)def

- (unsigned long long)unsignedLongLongValueForKey:(NSString *)key default:(unsigned long long)def

- (float)floatValueForKey:(NSString *)key default:(float)def 

- (double)doubleValueForKey:(NSString *)key default:(double)def

- (NSInteger)integerValueForKey:(NSString *)key default:(NSInteger)def

- (NSUInteger)unsignedIntegerValueForKey:(NSString *)key default:(NSUInteger)def

- (NSNumber *)numberValueForKey:(NSString *)key default:(NSNumber *)def

- (NSString *)stringValueForKey:(NSString *)key default:(NSString *)def

以上方法比较简单不做介绍

针对NSMutableDictionary 

+ (NSMutableDictionary *)dictionaryWithPlistData:(NSData *)plist

+ (NSMutableDictionary *)dictionaryWithPlistString:(NSString *)plist

读取plist

- (id)popObjectForKey:(id)aKey

这个方法是pop akey 并且获取

- (NSDictionary *)popEntriesForKeys:(NSArray *)keys

这个方式是pop keys 数组,并且获取keys 组成的数组dic

9.NSDate+YYAdd

对日期的操作 

- (NSInteger)year

- (NSInteger)month

- (NSInteger)day

- (NSInteger)hour

- (NSInteger)minute

- (NSInteger)second

- (NSInteger)nanosecond

- (NSInteger)weekday

- (NSInteger)weekdayOrdinal

- (NSInteger)weekOfMonth

- (NSInteger)weekOfYear

- (NSInteger)yearForWeekOfYear

- (NSInteger)quarter

- (BOOL)isLeapMonth  闰月

-- (BOOL)isLeapYear 闰年

- (BOOL)isToday  当天?

- (BOOL)isYesterday 昨天

- (NSDate *)dateByAddingYears:(NSInteger)years  增加几年

- (NSDate *)dateByAddingMonths:(NSInteger)months 增加几个月

- (NSDate *)dateByAddingWeeks:(NSInteger)weeks 增加几个周

- (NSDate *)dateByAddingDays:(NSInteger)days

- (NSDate *)dateByAddingHours:(NSInteger)hours

- (NSDate *)dateByAddingMinutes:(NSInteger)minutes

- (NSDate *)dateByAddingSeconds:(NSInteger)seconds

- (NSString *)stringWithFormat:(NSString *)format /// 获取格式字符串

- (NSString *)stringWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale   

NSTimeZone 代表时区 NSLocale  区域

- (NSString *)stringWithISOFormat  标准时间

+ (NSDate *)dateWithString:(NSString *)dateString format:(NSString *)format 日期字符串转日期

+ (NSDate *)dateWithString:(NSString *)dateString format:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale

+ (NSDate *)dateWithISOFormatString:(NSString *)dateString


10.NSNotificationCenter+YYAdd

给通知增加几个方法

+ (void)_yy_postNotification:(NSNotification *)notification 发通知

+ (void)_yy_postNotificationName:(NSDictionary *)info 从info获取通知信息发通知

- (void)postNotificationOnMainThread:(NSNotification *)notification 主线程发通知

- (void)postNotificationOnMainThread:(NSNotification *)notification waitUntilDone:(BOOL)wait 是否等待发通知 这个一般用于异步线程中使用

- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object 发通知给name

其他大同小异不做介绍


11.NSKeyedUnarchiver+YYAdd

+ (id)unarchiveObjectWithData:(NSData *)data exception:(__autoreleasing NSException **)exception

+ (id)unarchiveObjectWithFile:(NSString *)path exception:(__autoreleasing NSException **)exception

序列号,加的保护


12.NSTimer+YYAdd

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats

计时器封装成block 形式了

13.NSBundle+YYAdd

14.NSThread+YYAdd

+ (void)addAutoreleasePoolToCurrentRunloop 

给 currentLoop 增加自动释放池

主要是这个函数

static void YYRunloopAutoreleasePoolSetup() {

CFRunLoopRef runloop = CFRunLoopGetCurrent();

CFRunLoopObserverRef pushObserver;

pushObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopEntry,

true,        // repeat

-0x7FFFFFFF,  // before other observers

YYRunLoopAutoreleasePoolObserverCallBack, NULL);

CFRunLoopAddObserver(runloop, pushObserver, kCFRunLoopCommonModes);

CFRelease(pushObserver);

CFRunLoopObserverRef popObserver;

popObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit,

true,        // repeat

0x7FFFFFFF,  // after other observers

YYRunLoopAutoreleasePoolObserverCallBack, NULL);

CFRunLoopAddObserver(runloop, popObserver, kCFRunLoopCommonModes);

CFRelease(popObserver);

}

这个是增加两个个runloop 观察者 一个push 操作,一个pop操作

kCFRunLoopEntry代表runloop 创建run的时候  只执行一次,(cmd+shift+0)

kCFRunLoopEntry

The entrance of the run loop, before entering the event processing loop. This activity occurs once for each call toCFRunLoopRunandCFRunLoopRunInMode.

kCFRunLoopBeforeWaiting  当runloop 空闲的时候执行

kCFRunLoopExit当 runloop 退出的时候


static void YYRunLoopAutoreleasePoolObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {

switch (activity) {

case kCFRunLoopEntry: {

YYAutoreleasePoolPush();

} break;

case kCFRunLoopBeforeWaiting: {

YYAutoreleasePoolPop();

YYAutoreleasePoolPush();

} break;

case kCFRunLoopExit: {

YYAutoreleasePoolPop();

} break;

default: break;

}

}

从这个函数看出来,当runloop create run的时候就开始push

而在等待过程中 就pop 再push

退出的时候就push

接下来我们看看push操作干了啥事情

static inline void YYAutoreleasePoolPush() {

NSMutableDictionary *dic =  [NSThread currentThread].threadDictionary;

NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];

if (!poolStack) {

/*

do not retain pool on push,

but release on pop to avoid memory analyze warning

*/

CFArrayCallBacks callbacks = {0};

callbacks.retain = PoolStackRetainCallBack;

callbacks.release = PoolStackReleaseCallBack;

poolStack = (id)CFArrayCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);

dic[YYNSThreadAutoleasePoolStackKey] = poolStack;

CFRelease(poolStack);

}

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // create

[poolStack addObject:pool]; // push

}


每个线程都有个线程字典

NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;

这个字典可以保存下我们自定义的值

1.push操作就是读取字典threadDictionary的 key是 YYNSThreadAutoleasePoolStackKey的值

2.要是没有的话就创建cfarry  保存到改key 下

3.创建一个NSAutoreleasePool 将其加入到创建的数组中

其实数组相当于一个栈

再看看pop操作

static inline void YYAutoreleasePoolPop() {

NSMutableDictionary *dic =  [NSThread currentThread].threadDictionary;

NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];

[poolStack removeLastObject]; // pop

}

就是从数组中移除最后一个值

因此NSThread addAutoreleasePoolToCurrentRunloop函数就明白了

1调用改函数的时候我们会自动创建一个runloop

2当runloop 创建并run的时候我们创建一个自动释放池

3.当runloop 退出的时候我们销毁自动释放池

4自动释放池什么时候释放对象呢,如果runloop 每次循环玩还有空闲的时间的话我们就释放掉自动释放池中的对象。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容