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 每次循环玩还有空闲的时间的话我们就释放掉自动释放池中的对象。

推荐阅读更多精彩内容