YYKit-YYModel分析

1. YYModel 转化流程是怎么样的?

1. 由模型类封装YYModelMeta

首先,YYModel将模型类进行一次YYClassInfo信息封装,其中包括对变量封装(YYClassIvarInfo),对属性封装(YYClassProperityInfo),对成员方法封装(YYClassMethodInfo)。

其次,YYModel通过给用户提供的映射协议,让用户根据模型实现映射协议,同时基于封装的YYClassPeoperityInfo,最终封装成YYModelPropertyMeta。而在YYModelPropertyMeta中已经初始化了对应的映射内容。

最后,由YYClassInfo、已经形成映射关系的属性YYModelPropertyMeta数组等封装成了YYModelMeta。

2. 根据YYModelMeta,将dic内容转化为模型对象内容

根据YYModelMeta中的属性进行setter方法的objc_MsgSend调用,从而最终将值赋予对应的属性。

2.YYModel如何封装YYClassInfo的呢?

YYClassInfo组合了YYClassIvarInfo、YYClassMethodInfo、YYClassProperityInfo。其分别表示类的成员变量信息、类的方法信息、类的属性信息。以下将分别对方法、属性、变量进行分析。

类的方法列表—YYClassMethodInfo分析:
//通过类名,获取到类的实例方法
Method *methods = class_copyMethodList(cls,    &methodCount);
if (methods) {
    NSMutableDictionary *methodInfos = [NSMutableDictionary new];
    _methodInfos = methodInfos;

    //分别将方法信息解析到YYClassMethodInfo中
    for (unsigned int i = 0; i < methodCount; i++) {
        YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];

        //将方法名与YYClassMethodInfo以键值对的形式保存起来
        if (info.name) methodInfos[info.name] = info;
    }

    //注意,对于c层次的变量,我们需要使用free释放
    free(methods);
}
其中ClassMethodInfo就包括类方法的Method、Sel、Imp、方法参数类型数组、方法返回类型等等。
- (instancetype)initWithMethod:(Method)method {
    if (!method) return nil;

    self = [super init];    

    _method = method;
    _sel = method_getName(method);
    _imp = method_getImplementation(method);
    const char *name = sel_getName(_sel);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }

    //获取方法的编码字符串,它是一串由返回值和参数拼接而成。
    const char *typeEncoding = method_getTypeEncoding(method);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    }

    ----注解:(我们举一个例子来分析方法的类型编码对应的意思:
     NSLog(@"%s",method_getTypeEncoding(class_getInstanceMethod([TestModel class], @selector(hide:))));

     输出:v24@0:8@16,其中v表示返回类型为void24表示方法的所有参数占据的总字节数,@0表示方法对象中在offset为0的地方有一个对象参数(self对象),:8表示在方法对象中在offset为8字节的地方有个sel参数(方法自己),@16表示在方法对象中在offset为16个字节的位置有对象类型的参数。)

    //获取方法返回类型
    char *returnType = method_copyReturnType(method); 
    if (returnType) {
        _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
        free(returnType); //释放对应的对象
    }
    unsigned int argumentCount = method_getNumberOfArguments(method);
    if (argumentCount > 0) {
        NSMutableArray *argumentTypes = [NSMutableArray new];
        for (unsigned int i = 0; i < argumentCount; i++) {

            //获取方法参数类型(默认情况下,方法存在self和command两个隐藏的参数,分表表示方法类和方法自己
            char *argumentType = method_copyArgumentType(method, i);
            NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
            [argumentTypes addObject:type ? type : @""];
            if (argumentType) free(argumentType);
        }

        //最终将方法参数类型保存起来。
        _argumentTypeEncodings = argumentTypes;
    }

其中获取的参数类型可以比对以下编码格式表格:
image

获取类的属性列表—–YYClassProperityInfo
//获取类的属性列表
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
    NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
    _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);
}

其中YYClassPropertyInfo的创建过程如下:

- (instancetype)initWithProperty:(objc_property_t)property {
    if (!property) return nil;
    self = [super init];
    _property = property;

    //获取属性名
    const char *name = property_getName(property);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }

    YYEncodingType type = 0;
    unsigned int attrCount;

    //获取属性特性对象
    objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
    for (unsigned int i = 0; i < attrCount; i++) {
        switch (attrs[i].name[0]) {

            //表示属性的类型,它是以kv的形式存在
            case 'T': { // Type encoding
                if (attrs[i].value) {
                    _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];

                    //将类型转化为对应的类型
                    type = YYEncodingGetType(attrs[i].value);

                    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]) {
                            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': { // 实例变量
                if (attrs[i].value) {
                    _ivarName = [NSString stringWithUTF8String:attrs[i].value];
                }
            } break;
            case 'R': { //只读
                type |= YYEncodingTypePropertyReadonly;
            } break;
            case 'C': { //copy
                type |= YYEncodingTypePropertyCopy;
            } break;
            case '&': { //retain
                type |= YYEncodingTypePropertyRetain;
            } break;
            case 'N': { //非原子操作
                type |= YYEncodingTypePropertyNonatomic;
            } break;
            case 'D': {
                type |= YYEncodingTypePropertyDynamic;
            } break;
            case 'W': { //weak
                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 = 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;
}

属性结构如下:

struct property_t {
    const char *name;
    const char *attributes;
};

其中name表示属性的名字,而attributes表示一串用‘,’分割的属性特性字符串,而attributes中‘,’分隔的字符串是以kv的形式展示的。其中objc_property_attribute_t结构如下:

typedef struct {
   const char * _Nonnull name;           /**< The name of the attribute */
   const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

objc_property_attribute_t中常用的name和value如下表示:

常用attribute   name  value
   nonatomic      "N"    ""
   strong/retain  "&"    ""
   weak           "W"    ""
  属性的类型type  "T"    "@TypeName", 比如"@\"NSString\""
属性对应的实例变量Ivar   "V" "Ivar_name", eg "_name"
        readonly  "R"   ""
          getter  "G"   "GetterName", eg"isRight"
          setter  "S"   "SetterName", eg"setName"
        assign/atomic   默认即为assign和retain    

我们举个例子例子分析:

@property (nonatomic,strong,readwrite) NSString * uid 

以上这个属性在运行时获取到的属性特性为:T@"NSString",&,N,V_uid,分别表示此属性是NSString类型,它是strong的,同时是非原子操作(nonatomic),同时属性对应的成员变量为_uid
私有成员变量—YYClassIvarInfo
//获取私有成员变量
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
if (ivars) {
    NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
    _ivarInfos = ivarInfos;
    for (unsigned int i = 0; i < ivarCount; i++) {
        YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
        if (info.name) ivarInfos[info.name] = info;
    }
    free(ivars);
}

YYModel中通过YYClassIvarInfo封装了ivar变量,具体获取变量操作如下:

- (instancetype)initWithIvar:(Ivar)ivar {
    if (!ivar) return nil;
    self = [super init];
    _ivar = ivar;

    //获取变量名
    const char *name = ivar_getName(ivar);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }

    //记录变量相对于当前类对象中的位置偏移量,单位byte
    _offset = ivar_getOffset(ivar); 

    //根据编码字符串获取到对应的变量类型
    const char *typeEncoding = ivar_getTypeEncoding(ivar);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];

        //针对类型编码过的类型,通过以下方法进行类型解码
        _type = YYEncodingGetType(typeEncoding);
    }

我们比对IVar结构体:

struct ivar_t {

   int32_t *offset;
   const char *name;
   const char *type;
   // alignment is sometimes -1; use alignment() instead
   uint32_t alignment_raw;
   uint32_t size;

   uint32_t alignment() const {
       if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
      return 1 << alignment_raw;
   }

};
封装的YYClassIvarInfo是和ivar_t中结构一一对应的(除了内存对齐)

YYModelMeta是如何创建的呢

@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo; //类封装信息
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper; //key为属性(没有映射字段)/映射字段,value是YYModelPropertyMeta
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas; //保存所有YYModelPropertyMeta数组
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas; //保存映射key是 keyPath形式的YYModelPropertyMeta
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;//保存映射key是多个的对应的modelPropertyMeta
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;

BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}

//进行元数据的初始化过程
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;

//使用Foundation层的字典效率相对Cocoa层面的高
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;

}

以下我们将分析一下_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;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
    genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
    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;
            //由于[Classxx  class]返回的是类对象的类,即元类
            if (class_isMetaClass(meta)) {
                tmp[key] = obj;
            } else if ([obj isKindOfClass:[NSString class]]) {
                Class cls = NSClassFromString(obj);
                if (cls) {
                    tmp[key] = cls;
                }
            }
        }];
        genericMapper = tmp;
    }
}

//创建模型属性对象
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)

    //对YYClassPropertyInfo进行遍历
    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;

        //创建YYModelPropertyMeta对象
        _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                propertyInfo:propertyInfo
                                                                     generic:genericMapper[propertyInfo.name]];
        if (!meta || !meta->_name) continue;
        if (!meta->_getter || !meta->_setter) continue;
        if (allPropertyMetas[meta->_name]) continue;
        allPropertyMetas[meta->_name] = meta;
    }
    curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;

其中我们可以仔细看下YYModelPropertyMeta的创建过程:

+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {

    //对于容器类属性,存在generic类,但是如果不是容器类,而是协议属性,我们可以找到对应协议数组中的某个协议来作为属性的类型
    if (!generic && propertyInfo.protocols) {
        for (NSString *protocol in propertyInfo.protocols) {
            Class cls = objc_getClass(protocol.UTF8String);
            if (cls) {
                generic = cls;
                break;
            }
        }
    }

    //创建YYModelPropertyMeta对象并初始化
    _YYModelPropertyMeta *meta = [self new];
    meta->_name = propertyInfo.name; //属性名
    meta->_type = propertyInfo.type;//属性类型
    meta->_info = propertyInfo; //属性的YYClassPropertyInfo对象
    meta->_genericCls = generic; //如果是容器类对象和协议类对象

    //如果属性是对象类型,我们设置meta的基于foundation框架的对象类型。
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
        meta->_nsType = YYClassGetNSType(propertyInfo.cls);
    } else {

        //如果不是对象类型,则判断是否是数字类型
        meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
    }

    //如果不是对象,且不是数字类型,我们查看属性是否是结构体类型
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
        /*
         It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
         */
        //对于NSKeyedUnarchiver支持的结构体类型如下: 
        static NSSet *types = nil;
        static dispatch_once_t onceToken;
        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;
        }
    }
    meta->_cls = propertyInfo.cls;


    if (generic) {
        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
    } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
    }

    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;
        }
    }

    if (meta->_getter && meta->_setter) {
        /*
         KVC invalid type:
         kvc不支持long double和指针类型
         long double
         pointer (such as SEL/CoreFoundation object)
         */
        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: {
                //设置kvc兼容
                meta->_isKVCCompatible = YES;
            } break;
            default: break;
        }
    }

    return meta;
}

以上我们了解到propertyMeta设置了属性名,属性类型,如果是容器类属性,则设置了容器中的属性类型,设置foundation的类型,是否是数字类型,是否是支持的archive类型,setter,getter即kvc兼容等

以下我们分析下maper处理:

    //一对一的映射
    NSMutableDictionary *mapper = [NSMutableDictionary new];

    //含有属性.属性路径的映射,则存储在keyPathPropertyMetas中
    NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];

    //多个keys对应的映射数组
    NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];

    if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {

        //获取映射字典
        NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];

        //遍历映射字典
        [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {

            //设置propertyMeta相关映射属性
            _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
            if (!propertyMeta) return;

            //属性元数组中移除存在映射属性的PropertyMetas
            [allPropertyMetas removeObjectForKey:propertyName];

            //当mappedToKey是字符串类型
            if ([mappedToKey isKindOfClass:[NSString class]]) {
                if (mappedToKey.length == 0) return;

                propertyMeta->_mappedToKey = mappedToKey;
                NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
                for (NSString *onePath in keyPath) {
                    if (onePath.length == 0) {
                        NSMutableArray *tmp = keyPath.mutableCopy;
                        [tmp removeObject:@""];
                        keyPath = tmp;
                        break;
                    }
                }

                //如果keyPath的count大于1,设置属性元的映射keypath,并将设置了keypath的属性元添加到keypath属性元数组中
                if (keyPath.count > 1) {
                    propertyMeta->_mappedToKeyPath = keyPath;
                    [keyPathPropertyMetas addObject:propertyMeta];
                }

                //如果多个propertyMeta的key一样,我们使用链表来连接
                propertyMeta->_next = mapper[mappedToKey] ?: nil;

                //设置mapper字典
                mapper[mappedToKey] = propertyMeta;

            } else if ([mappedToKey isKindOfClass:[NSArray class]]) {

                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];
                    }

                    //根据属性映射mappedToKey是否设置来进行mappedToKey和mapedToKeyPaths设置
                    if (!propertyMeta->_mappedToKey) {
                        propertyMeta->_mappedToKey = oneKey;
                        propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
                    }
                }
                if (!propertyMeta->_mappedToKey) return;

                //设置mappedToKeyArray属性
                propertyMeta->_mappedToKeyArray = mappedToKeyArray;

                //将多映射属性元添加到multikeyspropertymetas数组中
                [multiKeysPropertyMetas addObject:propertyMeta];

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

    //对于没有设置映射的属性,我们将mappedToKey设置为对应的属性名
    [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        propertyMeta->_mappedToKey = name;
        propertyMeta->_next = mapper[name] ?: nil;
        mapper[name] = propertyMeta;
    }];

    if (mapper.count) _mapper = mapper;
    if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
    if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;

以上就将属性元的映射key和映射keypath设置完成,同时也将存在keypath的属性元和存在多个映射key的属性元添加进入对应的数组。

YYModel是如何根据YYModelMeta解析Dic

- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {

    //字典检测
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;

    //根据类对象获取到对应的YYModelMeta
    _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);

    //根据模型的属性个数和字典中key的个数比较来选择对应循环模型或者字典进行赋值操作
    if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {

        //字典中key少,则循环字典进行回调函数处理
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);

        //由于在执行ModelSetWithDictionaryFunction的时候,寻找对应的YYModelPropertyMeta的时候使用的是map中的key,而map中的key可能存在字符串、数数组,而dic中的key肯定是字符串,所以对于map中的key为数组的propertyMeta是没有处理的。
        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;
}


其中void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction CF_NOESCAPE applier, void *context);中,theArray表示要应用的数组,即_YYModelPropertyMetas数组。range表示应用的数组范围。applier表示数组中每个元素要调用的方法,这个方法就是字典中的字段设置到model中属性的一个函数。context表示上下文环境,即上文中创建的context,通过这个context可以获取到反转的字典等信息。

对于ModelSetWithPropertyMetaArrayFunction的实现,如下:

static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {

    //将_context转化为ModelSetContext类型
    ModelSetContext *context = _context;

    //从context中获取到转化的字典
    __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);

    //赋值通过属性的setter方法,所以判断setter是否存在
    if (!propertyMeta->_setter) return;
    id value = nil;

    //开始获取到属性对应的值
    if (propertyMeta->_mappedToKeyArray) {
        value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
    } else if (propertyMeta->_mappedToKeyPath) {
        value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
    } else {
        value = [dictionary objectForKey:propertyMeta->_mappedToKey];
    }

    //开始将值赋值到属性上
    if (value) {
        __unsafe_unretained id model = (__bridge id)(context->model);
        ModelSetValueForProperty(model, value, propertyMeta);
    }
}

我们开始分析下如何获取到属性对应的值:

3. YYModel如何保证代码的侵入性呢?

使用分类,实现了方法写入类方法列表的前面,防止了代码的侵入。

4. YYModel如何达到高性能呢?

  1. 使用CFDictionaryCreateMutable创建类缓存和元模型对象缓存,减少了计算类信息的次数,从而优化了性能
  2. 使用信号量,由于在信号量中操作的代码款是不需要等待的,所以信号量的性能相对是很高的。
  3. 对模型进行赋值的过程中,使用基于Core foundation底层数据结构,优化了性能。
  4. 在设置值的过程,使用属性的setter方法,而避免使用kvc,优化了性能。

问题点分析:

1. YYEncodingGetType(attrs[i].value);怎么理解?
2. 获取容器映射类的时候,为什么要用获取类的元类信息?

相关总结点:
1. 对于copy和mutableCopy操作是否创建新的内存空间,我们只需要理解copy操作是为了实现两个对象不相互影响的。所以可以总结为一下:
修改新对象(旧)对象,不影响旧对象(新)对象。如果旧对象是不可修改,则使用copy的话,系统会根据就对象的不可修改优化内存,从而产生浅拷贝,而如果对于旧对象的mutablecopy操作,则会导致新对象是可修改的,从而使得系统会创建新的内存空间,从而不相互影响。

  1. object_getClass(obj)和[xxx class]的区别:
    使用object_getClass返回的是对象的isa指针指向的类,而使用class则分情况:
    我们查看下[obj class]的源码
//对于使用了object_getClass返回的是对象的isa指针,即对象指向的类
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

//如果调用的是类的方法或者元类或者元类的类的class,则返回的是当前自己
+ (Class)class {
    return self;
}

//对于是类的对象是类的实例,调用的是实例方法,返回的是object_getClass指向的类指针。
- (Class)class {
    return object_getClass(self);
}
  1. CFArrayApplyFunction的作用?
    首先我们看下函数原型:

//其中theArray是要应用的数组,CFArrayApplierFunction表示每个元素要执行的函数,context表示执行的上下文,即YYModel中设置的context。
void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction CF_NOESCAPE applier, void *context)

我们来看下applier的实现函数:
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;

    //针对属性元中的映射key分别使用不同的方式获取key对应的值
    if (propertyMeta->_mappedToKeyArray) {

        value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
    } else if (propertyMeta->_mappedToKeyPath) {
        value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
    } else {
        value = [dictionary objectForKey:propertyMeta->_mappedToKey];
    }

    //如果值存在,那就可以开始使用ModelSetValueForProperty来给属性设置值了
    if (value) {
        __unsafe_unretained id model = (__bridge id)(context->model);
        ModelSetValueForProperty(model, value, propertyMeta);
    }
}

我们看下ModelSetValueForProperty(model, value, propertyMeta);的源码:

static void ModelSetValueForProperty(__unsafe_unretained id model,
                                     __unsafe_unretained id value,
                                     __unsafe_unretained _YYModelPropertyMeta *meta) {
    //判断属性是不是number类型,如果是则执行
    if (meta->_isCNumber) {

        NSNumber *num = YYNSNumberCreateFromID(value);
        ModelSetNumberToProperty(model, num, meta);
        if (num != nil) [num class]; // hold the number
    } else if (meta->_nsType) {
        if (value == (id)kCFNull) {
            ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
        } else {
            switch (meta->_nsType) {
                case YYEncodingTypeNSString:
                case YYEncodingTypeNSMutableString: {
                    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);
                        }
                    } else if ([value isKindOfClass:[NSNumber class]]) {
                        ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
                                                                       meta->_setter,
                                                                       (meta->_nsType == YYEncodingTypeNSString) ?
                                                                       ((NSNumber *)value).stringValue :
                                                                       ((NSNumber *)value).stringValue.mutableCopy);
        .............
}

对于赋值的过程使用了objc_msgSend执行setter函数。
为什么不使用kvc而使用setter进行赋值操作呢?

根据kvc的搜索逻辑,我们知道针对赋值操作需要一连串的查询逻辑,比较消耗性能。而YYModel则先判断setter函数是否存在,而后直接使用objc_msgSend进行函数调用赋值,这样减少了kvc的查询逻辑,提高了整体的性能。

YYModel中应用的知识点总结:
1. 运用runtime知识获取类的变量、属性、方法。对于属性的类型编码和方法的参数编码,运用编码对照表一一对照。

  1. 运用信号量实现线程安全:
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    需要同步的操作
    DISPATCH_SEMAPHORE_SIGNAL(lock)

  2. 使用CFDictionaryCreateMutable创建字典缓存,缓存封装好的YYClassInfo、YYModelMeta信息,因为这类信息在一般情况下不会改变,同时使用的次数会比较多,通过缓存可以减少创建的次数,优化了性能。

  3. 在使用字典或者数组循环处理的地方,使用CFDictionaryApplyFunction或者CFDictionaryApplyFunction。优化性能。

  4. 使用objc_msgSend为属性赋值,而避免使用kvc操作,优化了性能

苹果开源路径:
https://opensource.apple.com/tarballs/

参考文档:
https://www.jianshu.com/p/f4129b5194c0

猜你喜欢

转载自blog.csdn.net/lcpjoel/article/details/82587639