最近感觉知识匮乏,成长进步进度缓慢,早就听说YYKit屌到不行,遂来试着读一下,第一个先看YYModel
首先是一个NSObject的Category 和 一个类封装YYClassInfo
再来看一下封装类里面有YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo,YYClassInfo 分别是对Ivar Method , Property, Class的封装,本质上都是运用OCRunTime机制。具体可以了解的话请看.m实现里面,不一一列举。
接下来看一下YYModel的使用
+ (instancetype)modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];//这里是json 转 Dict
return [self modelWithDictionary:dic]; // 字典赋值给self 类的实例变量
}
// Json 转字典 应该没什么好说的 常用的[NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL]方法
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
return dic;
}
下面重点来看根据字典转模型
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil; // 以上为判空条件
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]; // 下面解释
if (modelMeta->_hasCustomClassFromDictionary) { // 如果有自己定义的类
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls; 类指向自己实现的返回类型
}
NSObject *one = [cls new]; //创建该类的实例对象
if ([one modelSetWithDictionary:dictionary]) return one; // 赋值成功返回
return nil; // 否则返回为空
}
有两个关键的东西1, _YYModelMeta 2, modelSetWithDictionary:
看下 _YYModelMeta这个类是什么东东。。。
@interface _YYModelMeta : NSObject {
YYClassInfo *_classInfo; // 类的信息 , 一开始的提到该类了 是对class的封装
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
NSArray *_allPropertyMetas; // 所有属性 已经做过了黑名单和白名单的处理。。。 以及所有的父类属性的遍历
NSArray *_keyPathPropertyMetas; // 所有的 KeyPath 属性 例如 a.b 这种的 只有自定义了映射类型才会有这种
NSArray *_multiKeysPropertyMetas; // 多个key指向同一个Value
NSUInteger _keyMappedCount; // _mapper.count
YYEncodingNSType _nsType; //编码类型。。。
// 以下几个变量 是根据类是否实现了具体方法
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
_YYModelMeta的快捷初始化方法
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil; // 判空
static CFMutableDictionaryRef cache;// 缓存 此处使用了CFFoundation里的字典 据说性能会高一些
static dispatch_once_t onceToken; // dispatch 里面只执行一次
static dispatch_semaphore_t lock; // dispatch 信号量 创建为1时 功效类似线程锁
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // 字典的初始化
lock = dispatch_semaphore_create(1); // 信号量初始化 如上所说value为1 ,多个线程只有一个能同时访问里面的代码 配合dispatch_semaphore_wait()开始 dispatch_semaphore_signal()结束 使用
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls)); // 缓存里面取 该类的ModelMeta信息 , 同时保证了线程安全
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) { // 如果不存在缓存 或者说是该类进行了变更 需要重新创建
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) { // 创建成功后 加锁 放入缓存
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
以上方法总结一下就是 实现了该类的信息缓存 还有保证了线程安全, 缓存是因为该类的信息占用内存很少,但是当有大量的格式需要映射时每次获取类信息比较消耗性能。。。
meta = [[_YYModelMeta alloc] initWithClass:cls];
这个为真正的_YYModelMeta初始化方法 内容很多,里面包含了对类信息的处理,类属性的处理 keypath处理 .遍历父类查找属性 ,详细了解课自行查找源码
接下来回到了这里
NSObject *one = [cls new]; if ([one modelSetWithDictionary:dictionary]) return one;
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic { // 初始化实例对象后的赋值操作
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO; // 判空
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)]; // 该方法介绍过 获取类信息 ,有缓存机制
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);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) { // 比较json的数据条目多,还是类的属性多,谁少就遍历谁,非常机智。。。
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context); //此方法为CFFoundation的字典遍历 参数分别为要遍历的字典, 遍历使用的方法, 传入的参数
if (modelMeta->_keyPathPropertyMetas) { // 如果有KeyPath 类型
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 { // 如果json数目很多 我们创建的对象属性不够 那就遍历属性
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction, // 这个方法实现里面 包含了KeyPath 和多对一的处理
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
以上的处理方法 最终会跳到赋值方法
ModelSetValueForProperty(model, value, propertyMeta);
第一天先粗略阅读 , 里面还有许多细节处理还没有细看, 而且有很多C方法需要慢慢研究琢磨,查询相关资料。。。