美文网首页
YYKit 学习笔记 (一)--- YYModel

YYKit 学习笔记 (一)--- YYModel

作者: David_Do | 来源:发表于2018-12-06 16:03 被阅读11次

    注:本文搬运地址:https://www.jianshu.com/p/62d4f86c7482

    YYModel --- 高性能 iOS/OSX 模型转换框架。
    https://github.com/ibireme/YYModel

    参考:
    学习mark
    https://www.jianshu.com/p/62d4f86c7482
    http://www.cnblogs.com/guohai-stronger/p/9647269.html

    实现解析

    重要的类

    • _YYModelMeta —————— 完成Model属性、方法、实例变量的解析,并生成与数据源相对应的字典映射;
    • YYClassInfo —————— 存储Model解析后的各种信息;
    • YYClassMethodInfo —————— 存储方法的信息;
    • YYClassIvarInfo —————— 存储实例变量的信息;
    • YYClassPropertyInfo —————— 存储属性的信息;
    • _YYModelPropertyMeta —————— 该类包含了属性的信息和设置属性时所需要的信息,有上述解析得到的信息和自定义的信息合成,用于第二步中的映射关系生成。

    实现步骤

    1、解析实体信息

    自定义的Model类调用modelWithDictionary:modelWithJSON:进行Model的初始化,接下来_YYModelMeta类将会为我们完成解析的工作。解析的目的是获取Model类的方法、属性、实例变量信息,这些信息将保存在YYClassInfo中。

    在解析之初会首先检查是否存在缓存,如果有缓存则直接返回缓存的_YYModelMeta对象。通常情况下,每一个类的属性、实例变量的解析只会进行一次,成功解析一次后的数据将会被缓存起来,只有当设置了YYClassInfo_needUpdate才会进行新的解析,也就是进行动态的添加属性、修改方法后需要更新。缓存数据保存在一个静态的CFMutableDictionaryRef字典当中,并通过dispatch_semaphore_t信号量来确保字典读取的线程安全性。

    没有缓存,则创建_YYModelMetaYYClassInfo对象,其中YYClassInfo也加入了缓存处理,个人觉得这里是没有必要的,因为_YYModelMeta已经有属性引用了YYClassInfo

    YYClassInfo_update会为我们完成具体的解析,该方法依次对方法、属性、实例变量进行了解析,个人发现方法、实例变量解析的信息并未用到。所以这里着重说一下属性的解析,通过class_copyPropertyList获取到属性列表,并为每一个属性生成一个YYClassPropertyInfo对象,该对象会保存在YYClassInfo_propertyInfos数组中。生成的YYClassPropertyInfo对象主要包含了属性名、setter、getter、属性数据类型等,这些信息都有对应的runtime方法。其中核心在于利用类型编码完成属性数据类型的解析。

    解析属性的核心代码:
    /// 1.获取Model类的属性列表
     unsigned int propertyCount = 0;
        objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
        if (properties) {
            NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
            // 将生成的YYClassPropertyInfo对象保存在该数组中
            _propertyInfos = propertyInfos;
            for (unsigned int i = 0; i < propertyCount; i++) {
                YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
                if (info.name) propertyInfos[info.name] = info;
            }
            // 记得释放内存
            free(properties); 
        }
    
    /// 2.初始化YYClassPropertyInfo对象
    - (instancetype)initWithProperty:(objc_property_t)property {
        if (!property) return nil;
        self = [super init];
        _property = property;
        
        // 获取属性的名称
        const char *name = property_getName(property);
        if (name) {
             // 将属性名称有C字符串转化为NSString
            _name = [NSString stringWithUTF8String:name];
        }
        
        YYEncodingType type = 0;
        unsigned int attrCount;
        // 获取属性对应的所有描述,即objc_property_attribute_t的结构体数组,包括原子性、数据类型、内存语义等
        objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
        for (unsigned int i = 0; i < attrCount; i++) {
            // objc_property_attribute_t的name为C字符串,name[0]表示获取第一个字符
            switch (attrs[i].name[0]) {
                case 'T': { // 这里将解析数据的类型,比如NSString、BOOL、NSArray等
                    if (attrs[i].value) {
                        _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
                        // 刚方法用于解析具体的数据类型
                        type = YYEncodingGetType(attrs[i].value);
                        
                        // 只有当数据类型为NSObject类,包括采纳了协议的属性,才需要如下进一步的解析
                        if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
                            NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
                            if (![scanner scanString:@"@\"" intoString:NULL]) continue;
                            
                            NSString *clsName = nil;
                            if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                                 // 将解析到的数据类型的类保存在_cls属性中
                                if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
                            }
                            
                            // 对于采纳协议的属性的解析
                            NSMutableArray *protocols = nil;
                            while ([scanner scanString:@"<" intoString:NULL]) {
                                NSString* protocol = nil;
                                if ([scanner scanUpToString:@">" intoString: &protocol]) {
                                    if (protocol.length) {
                                        if (!protocols) protocols = [NSMutableArray new];
                                        [protocols addObject:protocol];
                                    }
                                }
                                [scanner scanString:@">" intoString:NULL];
                            }
                            _protocols = protocols;
                        }
                    }
                } break;
                case 'V': { // Instance variable
                    if (attrs[i].value) {
                        _ivarName = [NSString stringWithUTF8String:attrs[i].value];
                    }
                } break;
                case 'R': {
                    type |= YYEncodingTypePropertyReadonly;
                } break;
                case 'C': {
                    type |= YYEncodingTypePropertyCopy;
                } break;
                case '&': {
                    type |= YYEncodingTypePropertyRetain;
                } break;
                case 'N': {
                    type |= YYEncodingTypePropertyNonatomic;
                } break;
                case 'D': {
                    type |= YYEncodingTypePropertyDynamic;
                } break;
                case 'W': {
                    type |= YYEncodingTypePropertyWeak;
                } break;
                case 'G': { // 获取自定义的getter方法
                    type |= YYEncodingTypePropertyCustomGetter;
                    if (attrs[i].value) {
                        _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
                    }
                } break;
                case 'S': { // 获取自定义的setter方法
                    type |= YYEncodingTypePropertyCustomSetter;
                    if (attrs[i].value) {
                        _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
                    }
                } // break; commented for code coverage in next line
                default: break;
            }
        }
        if (attrs) {
            free(attrs);
            attrs = NULL;
        }
        
        // 保存获取的type以及验证_setter、_getter是否已经获取到,这三个要素在设置属性值时将会被用到
        _type = type;
        if (_name.length) {
            if (!_getter) {
                _getter = NSSelectorFromString(_name);
            }
            if (!_setter) {
                _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
            }
        }
        return self;
    }
    

    2、生成映射关系
    通过生成的YYClassInfo来建立与数据源之间的映射关系,也就是生成了一个以数据源字典的key为key,以_YYModelPropertyMeta对象为值的字典_mapper,核心类为_YYModelPropertyMeta,该类包含了属性的信息和用户自定义的信息,例如属性的setter、getter、对应数据源的key、子类容器等。
    在生成_mapper的过程中包括白名单、黑名单、容器类、自定义数据源的key与属性名映射关系的处理,具体可参见_YYModelMeta类的initWithClass:方法。
    3、设置属性值
    上述两个步骤均是在_YYModelMeta中完成的,第三部回到NSObject+YYModel分类中,用modelSetWithDictionary:方法完成属性值的设置。
    核心代码:

     /// 使用下面的函数,遍历字典并通过ModelSetWithDictionaryFunction函数完成Model属性值的设置
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        
    
    /// context为ModelSetContext结构体
    typedef struct {
        void *modelMeta;  ///  为步骤1、2完成后生成的_YYModelMeta对象
        void *model;      ///  要生成的实体类,相当于id类型
        void *dictionary; ///  数据源
    } ModelSetContext;
    
    /// 属性值的设置最终通过objc_msgSend函数实现,例如下列对NSString类型的属性进行设置
    if ([value isKindOfClass:[NSString class]]) {
                            if (meta->_nsType == YYEncodingTypeNSString) {
                                ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
                            } else {
                                ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
                            }
                        }
    

    相关文章

      网友评论

          本文标题:YYKit 学习笔记 (一)--- YYModel

          本文链接:https://www.haomeiwen.com/subject/vouocqtx.html