YYKit源码解读
依靠大神的肩膀
YYKit有多屌我就不废话了,上面的文章中各位大神都给出了自己的看法,本文只是记录一下YYKit中个人学习到的东西,如有失误,欢迎指正。
1. YYKitMacro
一个三目运算符,返回中间值
YY_CLAMP(_x_, _low_, _high_)
交换两个变量的值
YY_SWAP(_a_, _b_)
然后是一些断言,没什么可说的。
这个在分析YYKit--宏定义的使用文中有提到,解释的很详细,以及weakify和strongify的使用
YYSYNTH_DUMMY_CLASS(_name_)
下面这个牛X了,我们都知道正常情况下类别中是无法直接添加属性的,但可以通过runtime来实现,这两个宏可以使你在类别中直接添加属性
// 添加Objective-C类型属性
YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_)
// 添加C类型属性
YYSYNTH_DYNAMIC_PROPERTY_CTYPE(_getter_, _setter_, _type_)
后面还有一些封装的方法
// range转换
NSRange YYNSRangeFromCFRange(CFRange range)
CFRange YYCFRangeFromNSRange(NSRange range)
// time
YYBenchmark(void (^block)(void), void (^complete)(double ms))
NSDate *_YYCompileTime(const char *data, const char *time)
// GCD
dispatch_time_t dispatch_time_delay(NSTimeInterval second)
dispatch_time_t dispatch_walltime_delay(NSTimeInterval second)
dispatch_time_t dispatch_walltime_date(NSDate *date)
bool dispatch_is_main_queue()
void dispatch_async_on_main_queue(void (^block)())
void dispatch_sync_on_main_queue(void (^block)())
2. Foundation
这部分分别对NSObject, KVO, ARC, NSString, NSNumber, NSDate, NSData, NSArray, NSDictionary, NSNotificationCenter, NSKeyedUnarchiver, NSTimer, NSBundle, NSThread 进行了类别扩展
2.1 NSObject+YYAdd
// 发送消息,获取消息的返回值
- (nullable id)performSelectorWithArgs:(SEL)sel, ...;
// 几个方法,分别与线程和Delay相关
- (void)performSelectorWithArgs:(SEL)sel afterDelay:(NSTimeInterval)delay, ...;
- (nullable id)performSelectorWithArgsOnMainThread:(SEL)sel waitUntilDone:(BOOL)wait, ...;
- (nullable id)performSelectorWithArgs:(SEL)sel onThread:(NSThread *)thread waitUntilDone:(BOOL)wait, ...;
- (void)performSelectorWithArgsInBackground:(SEL)sel, ...;
- (void)performSelector:(SEL)sel afterDelay:(NSTimeInterval)delay;
Runtime是OC的一大特性,在实际开发中用到的大概就是通过runtime来获取属性列表,以及method swizzling!下面这几个方法是对runtime的封装,看名字都可以猜到实现方法!
// 实例方法交换
+ (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel;
// 类方法交换
+ (BOOL)swizzleClassMethod:(SEL)originalSel with:(SEL)newSel;
//runtime 设置关联
- (void)setAssociateValue:(nullable id)value withKey:(void *)key;
// 设置声明为weak的属性
- (void)setAssociateWeakValue:(nullable id)value withKey:(void *)key;
// 获取关联属性
- (nullable id)getAssociatedValueForKey:(void *)key;
// 移除关联属性
- (void)removeAssociatedValues;
深拷贝,这个有点黑的感觉,通常情况下,我们要实现深拷贝会调用copy方法,这里直接将对象的数据复制一份赋给新对象,绕过了中间过程,直接底层访问,从而达到加快编译速度的目的;这个思路贯穿整个YYKit。
- (id)deepCopy {
id obj = nil;
@try {
obj = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
}
@catch (NSException *exception) {
NSLog(@"%@", exception);
}
return obj;
}
- (id)deepCopyWithArchiver:(Class)archiver unarchiver:(Class)unarchiver;
2.2. NSString+YYAdd
md2, md4, md5......一大串加密方法,URL和HTML归档方法。
计算String 的size, 这个方法用到的可能性比较大!
// 计算String 的size, 这个方法用到的可能性比较大!
- (CGSize)sizeForFont:(UIFont *)font size:(CGSize)size mode:(NSLineBreakMode)lineBreakMode
- (CGFloat)widthForFont:(UIFont *)font;
- (CGFloat)heightForFont:(UIFont *)font width:(CGFloat)width;
这里面有个容错处理,很优雅的处理方式,再也不用判断string的length了,我承认我菜!
[self respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]
// 遍历字符串的元素
- (void)enumerateUTF32CharInRange:(NSRange)range usingBlock:(void (^)(UTF32Char char32, NSRange range, BOOL *stop))block;
2.3 NSThread+YYAdd
只有一个方法
+ (void)addAutoreleasePoolToCurrentRunloop {
if ([NSThread isMainThread]) return; // 当前线程为主线程时,直接返回,主线程自带autoreleasePool
NSThread *thread = [self currentThread];
if (!thread) return;
if (thread.threadDictionary[YYNSThreadAutoleasePoolKey]) return; // already added
YYRunloopAutoreleasePoolSetup(); // 进行配置,看下面
thread.threadDictionary[YYNSThreadAutoleasePoolKey] = YYNSThreadAutoleasePoolKey; // mark the state
}
配置AutoreleasePool, 在配置中,添加了两个观察者,当RunLoop的状态发生改变时,进行YYRunLoopAutoreleasePoolObserverCallBack回调,这部分大神自己管理的内存,所以是不能在ARC中调用的。
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状态发生改变时,向PoolStack中添加(push)或移除(pop)autoreleasePool。
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;
}
}
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
}
Pop
static inline void YYAutoreleasePoolPop() {
NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;
NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
[poolStack removeLastObject]; // pop
}
** 小结一下:** 在Foundation这部分中,其作者添加了很多方便的方法来,上文中只提到了其中的部分代码;像在NSArray, NSDictionary等其他类目中添加了从头plist文件中读取文件,添加,删除元素,倒叙,打乱排序一类的方法,各位可自行探索,没有什么难点,都可以看懂的!
3. YYModel
YYModel用来将网络请求到的数据转换为需要的model,具有以下特性:
- 优良的性能
- 自动类型转换
- 类型安全
- 不需要继承其他基础类
- 轻量级
// 创建一个YYTestModel类
@interface YYTestModel : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger uid;
@property (nonatomic, strong) NSDate *created;
@end
// 测试代码
- (void)yy_test
{
// 数据,可以是NSData,NSDictionary,NSString类型
NSDictionary *data = @{
@"uid":@123456,
@"name":@"Harry",
@"created":@"1965-07-31T00:00:00+0000"
};
// 数据转模型
YYTestModel *model = [YYTestModel modelWithJSON:data];
}
点modelWithJSON:进去,其思路通过_YYModelMeta类获取到属性信息,然后通过消息机制调用Setter方法进行赋值,前面通过一系列转换后拿到Dictionary,看核心方法
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
// 创建元类,元类中会包含model的属性信息
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
// 如果属性个数为0,则返回
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
// 对属性赋值,在ModelSetWithPropertyMetaArrayFunction方法内部通过消息机制调用setter方法,对属性进行赋值,下面的几个方法也类似
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
未完待续。。。