YYModel 源码阅读记录

最近感觉知识匮乏,成长进步进度缓慢,早就听说YYKit屌到不行,遂来试着读一下,第一个先看YYModel

项目Demo图

首先是一个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方法需要慢慢研究琢磨,查询相关资料。。。

猜你喜欢

转载自blog.csdn.net/evol_f/article/details/81905238