YYModel源码分析

作者: 乔克_叔叔 | 来源:发表于2017-01-04 14:54 被阅读1169次

    前言

    YYModel 是一个iOS JSON模型转化库,和其他一些同类型库相比,具有比较好的性能优势。本文会对YYModel的源码进行分析,具体用法作者ibireme在github中有提及。YYModel的目录结构很简单,只有两个类, NSObject+YYModelYYClassInfoYYClassInfo主要对根类NSObject 的 Ivar , Method, Property以及Class本身进行了封装,NSObject+YYModel 是 NSObject的分类,扩展了一些JSON模型转化的方法。下图一张YYmodel的整体结构图:

    YYModel 结构图.png

    YYClassInfo

    要点

    YYClassIvarInfo : 对 Class的Ivar进行了封装
    YYClassMethodInfo : 对 Class的Method进行了封装
    YYClassPropertyInfo : 对 Class的Property进行了封装
    YYClassInfo : 对Class进行了封装,包含了YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo

    变量类型编码

    说到变量,可能我们写代码中最常见的就是变量了。当我们自定义一个类时,首先总是会申明一些属性,而且每个属性都会带有一些修饰词。比如是否原子性,内存管理原则,只读性。这些都可以通过这个属性的property_getAttributes 方法获取,苹果为所有的类型,包括属性类型都有编码,具体可以查看苹果官方文档:类型编码, 苹果官方文档:属性类型。 下面是一个简单例子:

    1. 申明属性
    @interface Cat : NSObject
    @end
    
    @interface Person : NSObject
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,assign) NSInteger age;
    @property (nonatomic,strong) Cat *cat;
    - (NSDictionary *)getProperties;
    @end
    

    2.通过runtime 来获取所有属性

    #import "Person.h"
    #import <objc/runtime.h>
    @implementation Person
    - (NSDictionary *) getProperties{
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        unsigned int count = 0;
        objc_property_t *propertyList = class_copyPropertyList([self class], &count);
        for (NSUInteger  i = 0; i < count; i++) {
            const char *name = property_getName(propertyList[i]);
            const char *attribute = property_getAttributes(propertyList[i]);
            NSString *nameStr = [NSString stringWithUTF8String:name];
            NSString *attributeStr = [NSString stringWithUTF8String:attribute];
            dic[nameStr] = attributeStr;
        }
        return [dic copy];
    }
    
    @end
    

    3.打印结果

    2017-01-03 10:54:34.690 Properties[16941:881462] {
        age = "Tq,N,V_age";
        cat = "T@\"Cat\",&,N,V_cat";
        name = "T@\"NSString\",C,N,V_name";
    }
    

    我们可以看到属性的所有类型编码信息,其中第一个代表是这个变量的类型,以T开头,最后一个代表的是变量的名字,一般用V_属性名表示,中间的部分就是我们声明的修饰符。比如age的类型是 Tq,而在官方文档中q 代表了A long long,64bit下NSInteger的取值范围就是long == long long ,N代表了非原子性,变量名是_age。其他的@代表了OC类型 id ,cat类型即是T@"Cat",&代表了 这个变量是retain (ARC下strong相当于retain),C 代表了copy

    YYEncodingType

    根据类型编码自定义了类型枚举,包含了三个部分

    YYEncodingTypeMask : 0~8位的值,变量的数据类型
    YYEncodingTypeQualifierMask : 8~16位的值,变量的方法类型
    YYEncodingTypePropertyMask: 16~24位的值,变量的属性类型

    这里把枚举值分成三个部分,通过 枚举值 & 对应 Mask 取出对应的变量类型,区分不同类型部分。YYEncodingGetType 是根据变量的数据类型编码值获取自定义YYEncodingType

    Var Method Property

    YYClassIvarInfo:用于存取变量的信息

    /**
     Instance variable information.
     */
    @interface YYClassIvarInfo : NSObject
    @property (nonatomic, assign, readonly) Ivar ivar;              ///< ivar opaque struct
    @property (nonatomic, strong, readonly) NSString *name;         ///< Ivar's name
    @property (nonatomic, assign, readonly) ptrdiff_t offset;       ///< Ivar's offset
    @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
    @property (nonatomic, assign, readonly) YYEncodingType type;    ///< Ivar's type
    
    /**
     Creates and returns an ivar info object.
     
     @param ivar ivar opaque struct
     @return A new object, or nil if an error occurs.
     */
    - (instancetype)initWithIvar:(Ivar)ivar;
    @end
    
    

    Ivar是表示实例变量的类型,其实际是一个指向objc_ivar结构体的指针,其定义如下:

    struct objc_ivar {  
        charchar *ivar_name                              OBJC2_UNAVAILABLE;  
        charchar *ivar_type                              OBJC2_UNAVAILABLE;  
        int ivar_offset                                  OBJC2_UNAVAILABLE;  
    #ifdef __LP64__  
        int space                                        OBJC2_UNAVAILABLE;  
    #endif  
    }  
    

    运用runtime,name通过的ivar_getName获取,offset 通过ivar_getOffset获取,typeEncoding 通过 ivar_getTypeEncoding 获取,type 通过自定义方法 YYEncodingGetType获取。其中offset是变量的基地址偏移量,可以通过它来直接访问变量数据,下面是例子:

        Person *p = [[Person alloc]init];
    //    NSLog(@"%@",[p getProperties]);
        p.age = 20;
        Ivar age_ivar = class_getInstanceVariable([Person class], "_age");
        long *age_pointer = (long *)((__bridge void  *)(p) + ivar_getOffset(age_ivar));
        NSLog(@"age ivar offset = %td", ivar_getOffset(age_ivar));
        *age_pointer = 10;
        NSLog(@"%@", p);
    
    - (NSString *)description {
        NSLog(@"current pointer = %p", self);
        NSLog(@"age pointer = %p", &_age);
        return [NSString stringWithFormat:@"age = %zi", _age];
    }
    

    打印结果:

    2017-01-03 14:41:14.175 Properties[20934:1554728] age ivar offset = 16
    2017-01-03 14:41:14.176 Properties[20934:1554728] current pointer = 0x600000075140
    2017-01-03 14:41:14.176 Properties[20934:1554728] age pointer = 0x600000075150
    2017-01-03 14:41:14.177 Properties[20934:1554728] age = 10
    

    YYClassMethodInfo:用于存取方法的信息

    /**
     Method information.
     */
    @interface YYClassMethodInfo : NSObject
    @property (nonatomic, assign, readonly) Method method;                  ///< method opaque struct
    @property (nonatomic, strong, readonly) NSString *name;                 ///< method name
    @property (nonatomic, assign, readonly) SEL sel;                        ///< method's selector
    @property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation
    @property (nonatomic, strong, readonly) NSString *typeEncoding;         ///< method's parameter and return types
    @property (nonatomic, strong, readonly) NSString *returnTypeEncoding;   ///< return value's type
    @property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
    
    /**
     Creates and returns a method info object.
     
     @param method method opaque struct
     @return A new object, or nil if an error occurs.
     */
    - (instancetype)initWithMethod:(Method)method;
    @end
    

    Method 的信息同 Ivar 一样,通过runtime的 method相关方法获取,其他的一些信息同Ivar,主要来说一下 SELImp

    SEL: 方法ID,C字符串

    typedef struct objc_selector *SEL;

    /// Defines a method
    struct objc_method_description {
        SEL name;               /**< The name of the method */
        char *types;            /**< The types of the method arguments */
    };
    

    IMP:方法函数指针

    OC是动态语言,方法调用(也叫做消息发送)是在运行时动态绑定的,而非编译时。如何做到正确的调用指定的方法呢?这里就需要用到SEL和IMP。编译器会将消息发送转换成对objc_msgSend(void /* id self, SEL op, ... */ )方法的调用 。objc_msgSend方法根据对象的isa指针找到对象的类,通过在类中的调度表(dispatch table)中查找SEL 获得 IMP,精确执行指定方法。

    YYClassPropertyInfo: 用于存取属性的信息

    /**
     Property information.
     */
    @interface YYClassPropertyInfo : NSObject
    @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
    @property (nonatomic, strong, readonly) NSString *name;           ///< property's name
    @property (nonatomic, assign, readonly) YYEncodingType type;      ///< property's type
    @property (nonatomic, strong, readonly) NSString *typeEncoding;   ///< property's encoding value
    @property (nonatomic, strong, readonly) NSString *ivarName;       ///< property's ivar name
    @property (nullable, nonatomic, assign, readonly) Class cls;      ///< may be nil
    @property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
    @property (nonatomic, assign, readonly) SEL getter;               ///< getter (nonnull)
    @property (nonatomic, assign, readonly) SEL setter;               ///< setter (nonnull)
    
    /**
     Creates and returns a property info object.
     
     @param property property opaque struct
     @return A new object, or nil if an error occurs.
     */
    - (instancetype)initWithProperty:(objc_property_t)property;
    @end
    

    YYClassInfo: 对Class的封装,包含了上面三部分的信息

    /**
     Class information for a class.
     */
    @interface YYClassInfo : NSObject
    @property (nonatomic, assign, readonly) Class cls; ///< class object
    @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
    @property (nullable, nonatomic, assign, readonly) Class metaCls;  ///< class's meta class object
    @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
    @property (nonatomic, strong, readonly) NSString *name; ///< class name
    @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
    @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
    @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
    @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties
    

    元类

    其中有metaCls代表了元类。那什么是元类呢?下面是一张经典的类结构图

    isa.png

    在OC中,每个实例对象都有一个isa指针,它指向了对象的class。而这个class也同样有一个isa指针,它就是指向了它的元类,其实类也是一个对象,所以对象之于类的关系,就相当于类(类对象)之于其元类(类对象的类)的关系。那元类有什么用呢?我们都知道在OC中调用方法有实例方法和类方法。我们调用实例方法,就是通过isa指针找到指定的class,查找存储在class中的方法列表执行方法,所以元类的作用就是调用类方法时,通过查找保存在元类中的类方法执行方法的作用。那为什么不把所有方法都保存在类中,可能这样更加高效也节省资源吧,具体可以自己查找资料。

    在YYClassInfo中,有一个_update方法,用来更新类中存储的信息。

    初始化方法

    + (instancetype)classInfoWithClass:(Class)cls {
        if (!cls) return nil;
        static CFMutableDictionaryRef classCache;
        static CFMutableDictionaryRef metaCache;
        static dispatch_once_t onceToken;
        static dispatch_semaphore_t lock;
        dispatch_once(&onceToken, ^{
            classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            lock = dispatch_semaphore_create(1);
        });
        dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
        YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
        if (info && info->_needUpdate) {
            [info _update];
        }
        dispatch_semaphore_signal(lock);
        if (!info) {
            info = [[YYClassInfo alloc] initWithClass:cls];
            if (info) {
                dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
                CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
                dispatch_semaphore_signal(lock);
            }
        }
        return info;
    }
    

    要点:

    • CFDictionaryCreateMutable 和 CFDictionarySetValue 不是线程安全的,所以需要创建锁,采用 dispatch_semaphore 通过控制信号量实现了锁

    • 设置缓存,如果在缓存中存在class,则直接获取到对应的ivar,method,property,否者创建YYClassInfo实例对象

    NSObject+YYModel:

    NSObject+YYModel是YYModel的核心类,主要部分:

    强制内联C函数:功能函数
    私有类_YYModelPropertyMeta : 管理Model属性的数据, 类型, 映射的key,keyPath
    私有类 _YYModelMeta :管理Model 数据,类型,存储 映射key,keypath,_YYModelPropertyMeta
    NSObject NSArray NSDictionary (YYModel) : 几个分类,YYModel主体功能实现
    YYModel 协议:扩展功能实现

    私有类_YYModelPropertyMeta

    _YYModelPropertyMeta是对上面的 YYClassPropertyInfo 的进一步封装。

    内部实例变量

    /// A property info in object model.
    // model property的进一步分装
    @interface _YYModelPropertyMeta : NSObject {
        @package
        NSString *_name;              //属性名
        YYEncodingType _type;         //属性的编码类型
        YYEncodingNSType _nsType;     //属性的Foundation类型
        BOOL _isCNumber;              //是否c语言的数字
        Class _cls;                   //属性的class
        Class _genericCls;    //属性内包含的类class
        SEL _getter;             //属性 getter方法
        SEL _setter;             //属性 setter方法
        BOOL _isKVCCompatible;     //是否可以使用KVC
        BOOL _isStructAvailableForKeyedArchiver;   //是否struct并且可以归档
        BOOL _hasCustomClassFromDictionary;   //是否包含本本地的class转换
        /*
         property->key:     
       _mappedToKey:key   _mappedToKeyPath:nil   _mappedToKeyArray:nil
         property->keyPath:  
       _mappedToKey:keyPath _mappedToKeyPath:keyPath(array)  _mappedToKeyArray:nil
         property->keys:   
       _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath        _mappedToKeyArray:keys(array)
         */
        NSString *_mappedToKey;     //属性名映射的 key 
        NSArray *_mappedToKeyPath;  //属性名映射的 keyPath 
        NSArray *_mappedToKeyArray;  //属性名的映射的key keyPath 数组 
        YYClassPropertyInfo *_info;  //属性的YYClassPropertyInfo info
        _YYModelPropertyMeta *_next;  
        //多个属性名映射到同一个key 时,指向下一个属性名的YYModelPropertyMeta 指针
    }
    @end
    

    主要是最后几个变量。其中_mappedToKey _mappedToKeyPath _mappedToKeyArray 是属性映射的key,keyPath ,key (keypath) 数组_mappedToKeyArray 中可以是key和keyPath,实际取其中第一个映射到的值

    一般一个属性名对应一个key值,如果多个属性名对应同一个key,这里就需要next发挥作用了。比如

    {
        @"name1" : @"name",
        @"name2" : @"name",
        @"name3" : @"name",
    }
    

    首先name1最先得到映射,对mapKey进行赋值,取得json中的name字段进行赋值一系列操作,此时next指针为nil
    name2接着进行映射,对mapKey进行赋值,接着取得原来json key对应的属性描述对象,将name2的next指针,指向name1。
    name3接着进行映射,对mapKey进行赋值,接着取得原来json key对应的属性描述对象,将name3的next指针,指向name2。

    代码中的实现

        propertyMeta->_next = mapper[mappedToKey] ?: nil;
        mapper[mappedToKey] = propertyMeta;
    

    初始化方法

    @implementation _YYModelPropertyMeta
    + (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
        //创建并且根据propertyInfo 进行变量赋值
        _YYModelPropertyMeta *meta = [self new];
        meta->_name = propertyInfo.name;
        meta->_type = propertyInfo.type;
        meta->_info = propertyInfo;
    
        // 属性为容器类型的时候, 映射类型赋值
        meta->_genericCls = generic;
    
        if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { 
             // 是否Foundation 类型
            meta->_nsType = YYClassGetNSType(propertyInfo.cls);
        } else { 
            // 是否C数据类型
            meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
        }
    
        // 属性为结构体类型
        if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
            /*
             It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
             */
            static NSSet *types = nil;
            static dispatch_once_t onceToken;
            // 单例 创建C结构体类型映射
            dispatch_once(&onceToken, ^{
                NSMutableSet *set = [NSMutableSet new];
                // 32 bit
                [set addObject:@"{CGSize=ff}"];
                [set addObject:@"{CGPoint=ff}"];
                [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
                [set addObject:@"{CGAffineTransform=ffffff}"];
                [set addObject:@"{UIEdgeInsets=ffff}"];
                [set addObject:@"{UIOffset=ff}"];
                // 64 bit
                [set addObject:@"{CGSize=dd}"];
                [set addObject:@"{CGPoint=dd}"];
                [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
                [set addObject:@"{CGAffineTransform=dddddd}"];
                [set addObject:@"{UIEdgeInsets=dddd}"];
                [set addObject:@"{UIOffset=dd}"];
                types = set;
            });
    
            // 只有上面结构体才能被归档
            if ([types containsObject:propertyInfo.typeEncoding]) {
                meta->_isStructAvailableForKeyedArchiver = YES;
            }
        }
        // 设置class类型
        meta->_cls = propertyInfo.cls;
    
        // 如果是容器类型
        if (generic) {
            // 从容器class 中读取
            meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
        } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
            // 从class类型中读取
            meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
        }
    
        // 设置 getter 和 setter 方法
        if (propertyInfo.getter) {
            if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
                meta->_getter = propertyInfo.getter;
            }
        }
        if (propertyInfo.setter) {
            if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
                meta->_setter = propertyInfo.setter;
            }
        }
    
        /**
         *  只有实现了getter和setter方法 才能实现归档
         */
        if (meta->_getter && meta->_setter) {
            /*
            类型是否支持 KVC
             */
            switch (meta->_type & YYEncodingTypeMask) {
                case YYEncodingTypeBool:
                case YYEncodingTypeInt8:
                case YYEncodingTypeUInt8:
                case YYEncodingTypeInt16:
                case YYEncodingTypeUInt16:
                case YYEncodingTypeInt32:
                case YYEncodingTypeUInt32:
                case YYEncodingTypeInt64:
                case YYEncodingTypeUInt64:
                case YYEncodingTypeFloat:
                case YYEncodingTypeDouble:
                case YYEncodingTypeObject:
                case YYEncodingTypeClass:
                case YYEncodingTypeBlock:
                case YYEncodingTypeStruct:
                case YYEncodingTypeUnion: {
                    meta->_isKVCCompatible = YES;
                } break;
                default: break;
            }
        }
        return meta;
    }
    @end
    
    

    私有类_YYModelMeta

    _YYModelMeta 是对 YYClassInfo 的再次封装

    内部变量

    
    @interface _YYModelMeta : NSObject {
        @package
        YYClassInfo *_classInfo;
    
        // key: 映射的 json key ,keyPath  value: _YYModelPropertyMeta 
        NSDictionary *_mapper;
    
        // model所有属性的PropertyMetas
        NSArray *_allPropertyMetas;
    
        //model所有映射son keyPath 属性 的PropertyMetas
        NSArray *_keyPathPropertyMetas;
    
        // model所有映射多个key 属性 的PropertyMetas
        NSArray *_multiKeysPropertyMetas;
        /// 需要映射的属性总个数
        NSUInteger _keyMappedCount;
    
        /// Model对应的Foundation 类型
        YYEncodingNSType _nsType;
    
        // 事否实现了自定义的映射关系表 
        BOOL _hasCustomWillTransformFromDictionary;
        BOOL _hasCustomTransformFromDictionary;
        BOOL _hasCustomTransformToDictionary;
        BOOL _hasCustomClassFromDictionary;
    }
    @end
    
    

    初始化

    /// Returns the cached model class meta
    + (instancetype)metaWithClass:(Class)cls {
        if (!cls) return nil;
        static CFMutableDictionaryRef cache;
        static dispatch_once_t onceToken;
        static dispatch_semaphore_t lock;
        dispatch_once(&onceToken, ^{
            cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            lock = dispatch_semaphore_create(1);
        });
        dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
        _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
        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;
    }
    

    首先从缓存中加载,没有在根据传入cls 创建meta,并做缓存处理, dispatch_semaphore 确保线程安全

    @implementation _YYModelMeta
    
    
    - (instancetype)initWithClass:(Class)cls {
    
        YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
        if (!classInfo) return nil;
        self = [super init];
    
        // 黑名单 会忽略返回数组里的属性
        NSSet *blacklist = nil;
        if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
            NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
            if (properties) {
                blacklist = [NSSet setWithArray:properties];
            }
        }
    
        // 白名单 只考虑返回数组内的属性 
        NSSet *whitelist = nil;
        if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
            NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
            if (properties) {
                whitelist = [NSSet setWithArray:properties];
            }
        }
    
        // 获取容器属性中的映射关系字典
        NSDictionary *genericMapper = nil;
       // 判断类中是否实现了对应的modelContainerPropertyGenericClass方法
        if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
    
            genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
          // 存储key和对应的class到字典中
            if (genericMapper) {
                NSMutableDictionary *tmp = [NSMutableDictionary new];
                [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                    if (![key isKindOfClass:[NSString class]]) return;
                    Class meta = object_getClass(obj);
                    if (!meta) return;
                    if (class_isMetaClass(meta)) {
                        tmp[key] = obj;
                    } else if ([obj isKindOfClass:[NSString class]]) {
                        Class cls = NSClassFromString(obj);
                        if (cls) {
                            tmp[key] = cls;
                        }
                    }
                }];
                genericMapper = tmp;
            }
        }
    
        // 存储所有属性的PropertyMeta对象
    
        NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
        YYClassInfo *curClassInfo = classInfo;
        while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
            // 遍历当前ClassInfo 中的所有PropertyInfo, 将它们封装成PropertyMeta
            for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
                // 检查是否合法和黑白名单筛选
                if (!propertyInfo.name) continue;
                if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
                if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
    
                // 通过propetyInfo来创建PropertyMeta 对象
                _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                        propertyInfo:propertyInfo
                                                                             generic:genericMapper[propertyInfo.name]];
                // meta name非空
                if (!meta || !meta->_name) continue;
                // 需要实现get方法和set方法
                if (!meta->_getter || !meta->_setter) continue;
                // 字典中已有该字段的meta 避免重复操作
                if (allPropertyMetas[meta->_name]) continue;
                allPropertyMetas[meta->_name] = meta;
            }
            // 遍历父类的property
            curClassInfo = curClassInfo.superClassInfo;
        }
      
        if (allPropertyMetas.count)
      _allPropertyMetas = allPropertyMetas.allValues.copy;
    
        // 创建 key :propertyMeta 映射关系字典
        NSMutableDictionary *mapper = [NSMutableDictionary new];
        NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
        NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
    
        // 是否实现自定义的映射表
        if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
            
            NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
    
            // 遍历自定义的字典
            [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
               
                _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
                if (!propertyMeta) return;
                // 由于用户自定义映射,把原来映射的数据删除
                [allPropertyMetas removeObjectForKey:propertyName];
    
                if ([mappedToKey isKindOfClass:[NSString class]]) { 
                   // key字段非空
                    if (mappedToKey.length == 0) return;
                    // 保存映射的key
                    propertyMeta->_mappedToKey = mappedToKey;
    
                    // 如果是keyPath的情况处理
                    NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
    
                    if (keyPath.count > 1) {  
                        propertyMeta->_mappedToKeyPath = keyPath;
                        [keyPathPropertyMetas addObject:propertyMeta];
                    }
    
                    // 多个属性映射同一个key的时候,用next存储前一个 json Key映射的meta
                    propertyMeta->_next = mapper[mappedToKey] ?: nil;
                    // 保存新的meta对象
                    mapper[mappedToKey] = propertyMeta;
    
                } else if ([mappedToKey isKindOfClass:[NSArray class]]) { 
    
             // 一个属性映射多个json Key
    
                    NSMutableArray *mappedToKeyArray = [NSMutableArray new];
                    for (NSString *oneKey in ((NSArray *)mappedToKey)) {
                        if (![oneKey isKindOfClass:[NSString class]]) continue;
                        if (oneKey.length == 0) continue;
    
                        NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
                        if (keyPath.count > 1) {
                            [mappedToKeyArray addObject:keyPath];
                        } else {
                            [mappedToKeyArray addObject:oneKey];
                        }
    
                        if (!propertyMeta->_mappedToKey) {
                            propertyMeta->_mappedToKey = oneKey;
                            propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
                        }
                    }
                    if (!propertyMeta->_mappedToKey) return;
    
                    propertyMeta->_mappedToKeyArray = mappedToKeyArray;
                    [multiKeysPropertyMetas addObject:propertyMeta];
    
                    propertyMeta->_next = mapper[mappedToKey] ?: nil;
                    mapper[mappedToKey] = propertyMeta;
                }
            }];
        }
    
        // 没有自定义映射规则的属性处理
    
        [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
            // 直接让mappedKey等于属性名
            propertyMeta->_mappedToKey = name;
            propertyMeta->_next = mapper[name] ?: nil;
            mapper[name] = propertyMeta;
        }];
    
        // 变量存储
        if (mapper.count) _mapper = mapper;
        if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
        if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
    
        _classInfo = classInfo;
        _keyMappedCount = _allPropertyMetas.count;
        _nsType = YYClassGetNSType(cls);
        _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
        _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
        _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
        _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
        return self;
    }
    
    

    JSON 转 Model

    + (instancetype)yy_modelWithJSON:(id)son {
        NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
        return [self yy_modelWithDictionary:dic];
    }
    

    传入的json可以是 NSDictionary, NSString , NSData_yy_dictionaryWithJSON 统一转化成字典

    + (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
        //字典合法性校验
        if (!dictionary || dictionary == (id)kCFNull) return nil;
        if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
        
        Class cls = [self class];
       //创建 model 元数据
        _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
       //自定义字典
        if (modelMeta->_hasCustomClassFromDictionary) {
            cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
        }
        // 根据dictionary 进行 model 属性赋值
        NSObject *one = [cls new];
        if ([one yy_modelSetWithDictionary:dictionary]) return one;
        return nil;
    }
    

    结构体 ModelSetContext 存储 modelMeta ,model, dic 作为 CFDictionaryApplyFunctionCFArrayApplyFunction 的context 参数,传递数据

    - (BOOL)yy_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)) {
            //映射的key Count >= dic Count
            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 {
             //映射的key Count < dic Count  
            CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                                 CFRangeMake(0, modelMeta->_keyMappedCount),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        
        if (modelMeta->_hasCustomTransformFromDictionary) {
            return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
        }
        return YES;
    }
    

    调用CoreFoundation 的 CFDictionaryApplyFunction 和 CFArrayApplyFunction 回调自定义的 Apply function
    static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context)static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context)

    根据获得的value ,model ,_propertyMeta 最后统一调用 下面方法,运用runtime的 objc_msgSend 设置model属性

    static void ModelSetValueForProperty(__unsafe_unretained id model,
                                         __unsafe_unretained id value,
                                         __unsafe_unretained _YYModelPropertyMeta *meta)
    

    自定义的CFDictionaryApplyFunction 的回调方法,CoreFoundation中的原回调函数
    typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);

    /**
     Apply function for dictionary, to set the key-value pair to model.
     
     @param _key     should not be nil, NSString.
     @param _value   should not be nil.
     @param _context _context.modelMeta and _context.model should not be nil.
     */
    static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
        ModelSetContext *context = _context;
        __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
        __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
        __unsafe_unretained id model = (__bridge id)(context->model);
        while (propertyMeta) {
            if (propertyMeta->_setter) {
                ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
            }
            propertyMeta = propertyMeta->_next;
        };
    }
    

    自定义的CFArrayApplyFunction 的回调方法,CoreFoundation中的原回调函数
    typedef void (*CFArrayApplierFunction)(const void *value, void *context);

    /**
     Apply function for model property meta, to set dictionary to model.
     
     @param _propertyMeta should not be nil, _YYModelPropertyMeta.
     @param _context      _context.model and _context.dictionary should not be nil.
     */
    static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
        ModelSetContext *context = _context;
        __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
        __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
        if (!propertyMeta->_setter) return;
        id value = nil;
        
        if (propertyMeta->_mappedToKeyArray) {
                //从dic中获取映射多个key的值,返回映射到的第一个值
            value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
        } else if (propertyMeta->_mappedToKeyPath) {
                //从dic中获取映射keypath的值
            value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
        } else {
                //从dic中获取映射key的值
            value = [dictionary objectForKey:propertyMeta->_mappedToKey];
        }
        
        if (value) {
                //Model 属性赋值
            __unsafe_unretained id model = (__bridge id)(context->model);
            ModelSetValueForProperty(model, value, propertyMeta);
        }
    }
    

    最终实现model 属性赋值。 该方法比较长,首先对 meta的属性类型进行判断,主要分为三类,

    • C基本数据类型
    • Foundation 类型
    • 其他类型,如 id, Class ,block,SEL等等
      根据类型获取对应value,

    最后都调用 ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value) 进行model 属性赋值

    static void ModelSetValueForProperty(__unsafe_unretained id model,
                                         __unsafe_unretained id value,
                                         __unsafe_unretained _YYModelPropertyMeta *meta)
    

    Model 转 JSON

    其中有效的JSON Object 只能是以下类型:
    NSArray,NSDictionary,NSString,NSNumber,NSNull。

    1.如果是NSDictionary,NSSet,NSArray 类型,递归调用此方法获得JSON Object
    2.如果是NSURL,NSAttributedString ,NSDate, NSData,做简单相应返回
    3.根据Model类型创建modelMeta,取实例变量_mapper 获取所有属性名和`propertyMeta,再根据propertyMeta的 类型_type 获得相应的 value, 根据有无_mappedToKeyPath再进一步处理,赋值,最后 判断有无自定义_hasCustomTransformToDictionary,返回最终转化结果

    主要方法:

    static id ModelToJSONObjectRecursive(NSObject *model) {
      
        if (!model || model == (id)kCFNull) return model;
        if ([model isKindOfClass:[NSString class]]) return model;
        if ([model isKindOfClass:[NSNumber class]]) return model;
        if ([model isKindOfClass:[NSDictionary class]]) {
            if ([NSJSONSerialization isValidJSONObject:model]) return model;
            NSMutableDictionary *newDic = [NSMutableDictionary new];
            [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
                NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
                if (!stringKey) return;
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (!jsonObj) jsonObj = (id)kCFNull;
                newDic[stringKey] = jsonObj;
            }];
            return newDic;
        }
        if ([model isKindOfClass:[NSSet class]]) {
            NSArray *array = ((NSSet *)model).allObjects;
            if ([NSJSONSerialization isValidJSONObject:array]) return array;
            NSMutableArray *newArray = [NSMutableArray new];
            for (id obj in array) {
                if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                    [newArray addObject:obj];
                } else {
                    id jsonObj = ModelToJSONObjectRecursive(obj);
                    if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
                }
            }
            return newArray;
        }
        if ([model isKindOfClass:[NSArray class]]) {
            if ([NSJSONSerialization isValidJSONObject:model]) return model;
            NSMutableArray *newArray = [NSMutableArray new];
            for (id obj in (NSArray *)model) {
                if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                    [newArray addObject:obj];
                } else {
                    id jsonObj = ModelToJSONObjectRecursive(obj);
                    if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
                }
            }
            return newArray;
        }
        if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
        if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
        if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
        if ([model isKindOfClass:[NSData class]]) return nil;
        
        _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
        if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
        NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
        __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
        [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
            if (!propertyMeta->_getter) return;
            
            id value = nil;
            if (propertyMeta->_isCNumber) {
                value = ModelCreateNumberFromProperty(model, propertyMeta);
            } else if (propertyMeta->_nsType) {
                id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                value = ModelToJSONObjectRecursive(v);
            } else {
                switch (propertyMeta->_type & YYEncodingTypeMask) {
                    case YYEncodingTypeObject: {
                        id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                        value = ModelToJSONObjectRecursive(v);
                        if (value == (id)kCFNull) value = nil;
                    } break;
                    case YYEncodingTypeClass: {
                        Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                        value = v ? NSStringFromClass(v) : nil;
                    } break;
                    case YYEncodingTypeSEL: {
                        SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                        value = v ? NSStringFromSelector(v) : nil;
                    } break;
                    default: break;
                }
            }
            if (!value) return;
            
            if (propertyMeta->_mappedToKeyPath) {
                NSMutableDictionary *superDic = dic;
                NSMutableDictionary *subDic = nil;
                for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
                    NSString *key = propertyMeta->_mappedToKeyPath[i];
                    if (i + 1 == max) { // end
                        if (!superDic[key]) superDic[key] = value;
                        break;
                    }
                    
                    subDic = superDic[key];
                    if (subDic) {
                        if ([subDic isKindOfClass:[NSDictionary class]]) {
                            subDic = subDic.mutableCopy;
                            superDic[key] = subDic;
                        } else {
                            break;
                        }
                    } else {
                        subDic = [NSMutableDictionary new];
                        superDic[key] = subDic;
                    }
                    superDic = subDic;
                    subDic = nil;
                }
            } else {
                if (!dic[propertyMeta->_mappedToKey]) {
                    dic[propertyMeta->_mappedToKey] = value;
                }
            }
        }];
        
        if (modelMeta->_hasCustomTransformToDictionary) {
            BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
            if (!suc) return nil;
        }
        return result;
    }
    

    参考资料

    郑钦洪_:YYModel 源码历险记

    相关文章

      网友评论

      本文标题:YYModel源码分析

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