YYKit 源码解析(5)-YYModel

终于把前面的base文件夹简简单单的看了一遍,终于可以回到正片上来了,保证不烂尾。

项目天天用yymodel解析数据,效率高也没看看到底为啥效率高,最近有时间,仔细看看。

NSObject+YYModel

YYClassInfo

这个类怎么学习呢?我从调用入口看是看起。

由于yykit 框架里面的yymodel 方法名没有标记yy_ 所以,我们使用的是yymodel 进行分析。

yymodel 类是NSObject 的category 

+ (instancetype)yy_modelWithJSON:(id)json {

NSDictionary *dic = [self _yy_dictionaryWithJSON:json];

return [self yy_modelWithDictionary:dic];

}


1.调用 + (NSDictionary *)_yy_dictionaryWithJSON:(id)json<1> 函数获取 dic

2 调用+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary<2> 返回结果



我们看<1> 的函数

+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {

if (!json || json == (id)kCFNull) return nil;

NSDictionary *dic = nil;

NSData *jsonData = nil;

if ([json isKindOfClass:[NSDictionary class]]) {

dic = json;

} else if ([json isKindOfClass:[NSString class]]) {

jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];

} else if ([json isKindOfClass:[NSData class]]) {

jsonData = json;

}

if (jsonData) {

dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];

if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;

}

return dic;

}

我们从这里看出来我们外界传入到该类的参数可以是NSDictionary NSString NSData

但是最后返回的结果是NSDictionary 



接下来我们看<2>出的函数

+ (instancetype)yy_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 yy_modelSetWithDictionary:dictionary]) return one;

return nil;

}

这个函数算是真正解析的入口了。我们看看这个方法都干了啥事情

1 第一步校验参数是否正确

2获取该类的class

3 通过_YYModelMeta 的类方法+ (instancetype)metaWithClass:(Class)cls <3>获取一个_YYModelMeta 对象

4 判断 _YYModelMeta对象是否 有_hasCustomClassFromDictionary标记为YES,有的话,cls 就调用下+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary <4> 方法 .要是返会cls 那么就给赋值,没有则用原来的cls

5 .创建对象

6 调用- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic <5>方法

7返回

这个函数有个新类_YYModelMeta

我们看看这个类干啥的

先看看这个类的属性

YYClassInfo *_classInfo;

/// Key:mapped key and key path, Value:_YYModelPropertyMeta.

NSDictionary *_mapper;

/// Array<_YYModelPropertyMeta>, all property meta of this model.

NSArray *_allPropertyMetas;

/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.

NSArray *_keyPathPropertyMetas;

/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.

NSArray *_multiKeysPropertyMetas;

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

属性先不做介绍,看源码实现。先调用这个函数


这里是<3 >处 的分析

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

}

这个函数干啥了呢?

1.生成一个单例缓存CFMutableDictionaryRef cache 和一个信号量

2.从缓存中查找key 是class的 _YYModelMeta

3.要是缓存中没有meta 或者meta 标记为需要更新

4. 通过- (instancetype)initWithClass:(Class)cls 方法创建meta 

5 将meta 更新到单例缓存数组中

总结下这个函数,这个函数就是从缓存获取解析完毕的_YYModelMeta 没有就解析该类

接下来看- (instancetype)initWithClass:(Class)cls 函数

- (instancetype)initWithClass:(Class)cls { YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]; if (!classInfo) return nil; self = [super init]; // Get black list NSSet *blacklist = nil; if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) { NSArray *properties = [(id)cls modelPropertyBlacklist]; if (properties) { blacklist = [NSSet setWithArray:properties]; } } // Get white list NSSet *whitelist = nil; if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) { NSArray *properties = [(id)cls modelPropertyWhitelist]; if (properties) { whitelist = [NSSet setWithArray:properties]; } } // Get container property's generic class NSDictionary *genericMapper = nil; if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) { genericMapper = [(id)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; if (class_isMetaClass(meta)) { tmp[key] = obj; } else if ([obj isKindOfClass:[NSString class]]) { Class cls = NSClassFromString(obj); if (cls) { tmp[key] = cls; } } }]; genericMapper = tmp; } } // Create all property metas. NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new]; YYClassInfo *curClassInfo = classInfo; while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy) 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 *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; // create mapper NSMutableDictionary *mapper = [NSMutableDictionary new]; NSMutableArray *keyPathPropertyMetas = [NSMutableArray new]; NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new]; if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) { NSDictionary *customMapper = [(id)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]]) {

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;

}

}

if (keyPath.count > 1) {

propertyMeta->_mappedToKeyPath = keyPath;

[keyPathPropertyMetas addObject:propertyMeta];

}

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

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

}

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) {

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;

}

这个方法我们看看都干啥了

1. 生成一个YYClassInfo 对象

2.要是 cla 实现 + (nullable NSArray*)modelPropertyBlacklist 方法了 ,回去改方法的数据

3.要是cls 实现+ (nullable NSArray*)modelPropertyWhitelist; 方法,临时变量保存改方法的返回值

4 要是cls 实现+ (NSDictionary *)modelContainerPropertyGenericClass 方法 。那么就对返回的数据 进行简单的处理,保存到临时变量中。(这里适配了 obj 是class 或者字符串两种形式。)

5.给临时数组allPropertyMetas 增加key是_YYModelPropertyMeta 的name 值是self 对象。这里的具体细节解析在下面分析,暂时标记为<6>

6. 变量_allPropertyMetas 指向临时变量allPropertyMetas 的所有value 值

7.要是cls实现了 + (NSDictionary *)modelCustomPropertyMapper ,那么调用cls调用+ (NSDictionary *)modelCustomPropertyMapper 方法。给临时变量mapper,keyPathPropertyMetas  ,multiKeysPropertyMetas 赋值。具体赋值看<7>出

8.修改临时变量allPropertyMetas 中的_YYModelPropertyMeta 相关属性

9.给变量_mapper,_keyPathPropertyMetas,_multiKeysPropertyMetas 赋值。

10给变量 _classInfo _keyMappedCount  _nsType 赋值。这里有个static force_inline YYEncodingNSType YYClassGetNSType(Class cls)<8> 方法。

11 给变量 _hasCustomWillTransformFromDictionary _hasCustomTransformFromDictionary _hasCustomTransformToDictionary _hasCustomClassFromDictionary 赋值 这四个参数分别标记是否有一个特定关联的函数

12 返回self

这个类关键处 YYClassInfo  _YYModelPropertyMeta  和 没有分析的标记的<6>出的地方


接下来先看 YYClassInfo  类

分析类成员变量


黄色代表 public 属性

粉红色代表 privite 变量

接下来我们看初始化

public方法提供了两个,只有类方法初始化,没有实例初始化

+ (instancetype)classInfoWithClassName:(NSString *)className

+ (instancetype)classInfoWithClass:(Class)cls

+ (instancetype)classInfoWithClassName:(NSString *)className {

Class cls = NSClassFromString(className);

return [self classInfoWithClass:cls];

}

这个函数就是将className 转换成class 再调用上面的初始化方法

+ (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;

}

这个函数干啥了呢

1.单例实例化一个classCache 字典 和metaCache 字典,还初始化了信号量

2. 检查class 是meta 还是 class ,是meta 从meta 字典去key 是cls 的值,不是meta ,那么从class 字典中取 key是cls的值。

3 要是从缓存中获取到了cls 值,并且cls标记为 需要更新,那么调用 - (void)_update <9>

4没有值,那么创建一个YYClassInfo ,并根据是否是meta保存到相应字典中去

5返回。

这个函数就是从缓存中获取class 信息,没有就创建一个,具体创建方法是这个房- (instancetype)initWithClass:(Class)cls

- (instancetype)initWithClass:(Class)cls {

if (!cls) return nil;

self = [super init];

_cls = cls;

_superCls = class_getSuperclass(cls);

_isMeta = class_isMetaClass(cls);

if (!_isMeta) {

_metaCls = objc_getMetaClass(class_getName(cls));

}

_name = NSStringFromClass(cls);

[self _update];

_superClassInfo = [self.class classInfoWithClass:_superCls];

return self;

}

这个方法干啥事情了呢?

1. 根据类的特性 给 变量_cls _superCls _isMeta _metaCls _name 赋值,

2.调用- (void)_update 方法

3.解析父类class ,将结果赋值给 _superClassInfo 结构如下



className 是解析的class

superClassInfo 保存的是super的解析的YYClassInfo 

类结构明白了。那么我们应该看看 <9> - (void)_update  这个类解析啥了

- (void)_update {

_ivarInfos = nil;

_methodInfos = nil;

_propertyInfos = nil;

Class cls = self.cls;

unsigned int methodCount = 0;

Method *methods = class_copyMethodList(cls, &methodCount);

if (methods) {

NSMutableDictionary *methodInfos = [NSMutableDictionary new];

_methodInfos = methodInfos;

for (unsigned int i = 0; i < methodCount; i++) {

YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];

if (info.name) methodInfos[info.name] = info;

}

free(methods);

}

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

}

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

}

if (!_ivarInfos) _ivarInfos = @{};

if (!_methodInfos) _methodInfos = @{};

if (!_propertyInfos) _propertyInfos = @{};

_needUpdate = NO;

}

1.获取self.cls 的所有method 

2. 用YYClassMethodInfo 解析method 的每一个方法,将YYClassMethodInfo 保存到到变量_methodInfos字典中,key是 YYClassMethodInfo 的name ,将字典

3.获取objc_property_t 列表,用YYClassPropertyInfo 解析每一个属性,将YYClassPropertyInfo 保存到变量_propertyInfos 中key 是YYClassPropertyInfo 的name

4同理,_ivarInfos 保存该类的Ivar ,将Ivar 封装在了YYClassIvarInfo,key是name

5.将_needUpdate 变量设置为NO

这个类里面出现了三个新的类YYClassMethodInfo,YYClassPropertyInfo,YYClassIvarInfo。

YYClassMethodInfo类


这个类的方法就一个

- (instancetype)initWithMethod:(Method)method;

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

}

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++) {

char *argumentType = method_copyArgumentType(method, i);

NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;

[argumentTypes addObject:type ? type : @""];

if (argumentType) free(argumentType);

}

_argumentTypeEncodings = argumentTypes;

}

return self;

}

1变量_method 保存method

2 变量_sel 保存method 的 name

3变量_imp 保存method 的 方法实现

4 获取 变量_sel的name 将其保存在 变量_name 属性中

5 获取method 的编码,保存在变量_typeEncoding 中

6 获取method 的返回值类型,保存在变量_returnTypeEncoding 中

7获取mthod的参数数量,变量获取每一个参数类型,将参数类型保存依次加入到参数数组中,让变量_argumentTypeEncodings 引用该参数数组。

8.返回self

YYClassMethodInfo 类就是解析了Method 的方方面面。

YYClassPropertyInfo


上图是YYClassPropertyInfo 的类结构

方法也就一个

- (instancetype)initWithProperty:(objc_property_t)property

- (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]) {

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': { // 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': {

type |= YYEncodingTypePropertyCustomGetter;

if (attrs[i].value) {

_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);

}

} break;

case 'S': {

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;

}

1 变量_property 保存objc_property_t 结构体

2获取 objc_property_t 的name 保存到  变量_name 中

3获取objc_property_t 结构体的 属性和数量

这里我们就应该了解属性的type 苹果官网的编码  还有一篇

You can use theproperty_getAttributesfunction to discover the name, the@encodetype string of a property, and other attributes of the property.

The string starts with aTfollowed by the@encodetype and a comma, and finishes with aVfollowed by the name of the backing instance variable. Between these, the attributes are specified by the following descriptors, separated by commas:


截图如下

这个表里面没有T T代表属性类型。

eg

@property(readonly) int intReadonly;

结果是

Ti,R,VintReadonly    Ti 代表int 类型,R 代表只读属性,V代表实例变量intReadonly

4.解析属性,给相关变量赋值 

5.将解析的type 赋值给_type 变量

6.要是没有设置get set 方法。设置get set 方法。 

7 返回self

这个函数的关键部分是上面介绍的第四步

这里分情况讨论objc_property_attribute_t 的name[0] 属性值

<1> name[0]=T

这里给_typeEncoding 变量赋值,值是objc_property_attribute_t  value 的字符串。

这里有个编码 YYEncodingType YYEncodingGetType(const char *typeEncoding)

返回结果是YYEncodingType

/**

Type encoding's type.

*/

typedef NS_OPTIONS(NSUInteger, YYEncodingType) {

YYEncodingTypeMask      = 0xFF, ///< mask of type value

YYEncodingTypeUnknown    = 0, ///< unknown

YYEncodingTypeVoid      = 1, ///< void

YYEncodingTypeBool      = 2, ///< bool

YYEncodingTypeInt8      = 3, ///< char / BOOL

YYEncodingTypeUInt8      = 4, ///< unsigned char

YYEncodingTypeInt16      = 5, ///< short

YYEncodingTypeUInt16    = 6, ///< unsigned short

YYEncodingTypeInt32      = 7, ///< int

YYEncodingTypeUInt32    = 8, ///< unsigned int

YYEncodingTypeInt64      = 9, ///< long long

YYEncodingTypeUInt64    = 10, ///< unsigned long long

YYEncodingTypeFloat      = 11, ///< float

YYEncodingTypeDouble    = 12, ///< double

YYEncodingTypeLongDouble = 13, ///< long double

YYEncodingTypeObject    = 14, ///< id

YYEncodingTypeClass      = 15, ///< Class

YYEncodingTypeSEL        = 16, ///< SEL

YYEncodingTypeBlock      = 17, ///< block

YYEncodingTypePointer    = 18, ///< void*

YYEncodingTypeStruct    = 19, ///< struct

YYEncodingTypeUnion      = 20, ///< union

YYEncodingTypeCString    = 21, ///< char*

YYEncodingTypeCArray    = 22, ///< char[10] (for example)

YYEncodingTypeQualifierMask  = 0xFF00,  ///< mask of qualifier

YYEncodingTypeQualifierConst  = 1 << 8,  ///< const

YYEncodingTypeQualifierIn    = 1 << 9,  ///< in

YYEncodingTypeQualifierInout  = 1 << 10, ///< inout

YYEncodingTypeQualifierOut    = 1 << 11, ///< out

YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy

YYEncodingTypeQualifierByref  = 1 << 13, ///< byref

YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway

YYEncodingTypePropertyMask        = 0xFF0000, ///< mask of property

YYEncodingTypePropertyReadonly    = 1 << 16, ///< readonly

YYEncodingTypePropertyCopy        = 1 << 17, ///< copy

YYEncodingTypePropertyRetain      = 1 << 18, ///< retain

YYEncodingTypePropertyNonatomic    = 1 << 19, ///< nonatomic

YYEncodingTypePropertyWeak        = 1 << 20, ///< weak

YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=

YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=

YYEncodingTypePropertyDynamic      = 1 << 23, ///< @dynamic

};

类型编码

YYEncodingTypeMask 编码区对应的是 类型编码 官网 

YYEncodingTypeQualifierMask 编码区对应的是 方法编码官网 最下方

YYEncodingTypePropertyMask 对应的属性编码 官网

接下来看字符串怎么转换的成编码的

YYEncodingType YYEncodingGetType(const char *typeEncoding) {

char *type = (char *)typeEncoding;

if (!type) return YYEncodingTypeUnknown;

size_t len = strlen(type);

if (len == 0) return YYEncodingTypeUnknown;

YYEncodingType qualifier = 0;

bool prefix = true;

while (prefix) {

switch (*type) {

case 'r': {

qualifier |= YYEncodingTypeQualifierConst;

type++;

} break;

case 'n': {

qualifier |= YYEncodingTypeQualifierIn;

type++;

} break;

case 'N': {

qualifier |= YYEncodingTypeQualifierInout;

type++;

} break;

case 'o': {

qualifier |= YYEncodingTypeQualifierOut;

type++;

} break;

case 'O': {

qualifier |= YYEncodingTypeQualifierBycopy;

type++;

} break;

case 'R': {

qualifier |= YYEncodingTypeQualifierByref;

type++;

} break;

case 'V': {

qualifier |= YYEncodingTypeQualifierOneway;

type++;

} break;

default: { prefix = false; } break;

}

}

len = strlen(type);

if (len == 0) return YYEncodingTypeUnknown | qualifier;

switch (*type) {

case 'v': return YYEncodingTypeVoid | qualifier;

case 'B': return YYEncodingTypeBool | qualifier;

case 'c': return YYEncodingTypeInt8 | qualifier;

case 'C': return YYEncodingTypeUInt8 | qualifier;

case 's': return YYEncodingTypeInt16 | qualifier;

case 'S': return YYEncodingTypeUInt16 | qualifier;

case 'i': return YYEncodingTypeInt32 | qualifier;

case 'I': return YYEncodingTypeUInt32 | qualifier;

case 'l': return YYEncodingTypeInt32 | qualifier;

case 'L': return YYEncodingTypeUInt32 | qualifier;

case 'q': return YYEncodingTypeInt64 | qualifier;

case 'Q': return YYEncodingTypeUInt64 | qualifier;

case 'f': return YYEncodingTypeFloat | qualifier;

case 'd': return YYEncodingTypeDouble | qualifier;

case 'D': return YYEncodingTypeLongDouble | qualifier;

case '#': return YYEncodingTypeClass | qualifier;

case ':': return YYEncodingTypeSEL | qualifier;

case '*': return YYEncodingTypeCString | qualifier;

case '^': return YYEncodingTypePointer | qualifier;

case '[': return YYEncodingTypeCArray | qualifier;

case '(': return YYEncodingTypeUnion | qualifier;

case '{': return YYEncodingTypeStruct | qualifier;

case '@': {

if (len == 2 && *(type + 1) == '?')

return YYEncodingTypeBlock | qualifier;

else

return YYEncodingTypeObject | qualifier;

}

default: return YYEncodingTypeUnknown | qualifier;

}

}

1检测参数是否正确

2 判断字符串是否是YYEncodingTypeQualifierMask 。给type 赋值

3 检测 字符串是否是YYEncodingTypeMask 给type赋值

这里没有对YYEncodingTypePropertyMask 区间进行检测


@property (assign) NSString * str

type = YYEncodingGetType(attrs[i].value);

这里 type 获取的是 NSString 的类型是 id

接下来,我们判断type 类型是不是id 类型的,是id 类型。用_typeEncoding 变量类型进一步校验。

@property (nonatomic ,strong) YYDelegateModel<UITabBarDelegate,UITableViewDelegate,UITableViewDataSource> * model;

2017-12-21 17:20:31.552278+0800 YYKitDemo[53387:12086693] @"YYDelegateModel<UITabBarDelegate><UITableViewDelegate><UITableViewDataSource>"

要是是id 类型,我们扫描从"到 “< 之间的值。这之间的值就是类值。赋值给变量_cls

剩余的部分是协议部分.每一个协议都是用<> 包含起来的。

由于协议可以多个,所以将其用数组承接。放入变量 _protocols 中

<2>name[0]=V

V 是成员变量的代表 value 的值成员变量的名字

<3> 其他的就是onlyRead ,nonatomic,Getting,Setting 等的特有的标示符号了。

这里get方法和setting 设置type 的时候同时给 _getter 和_setter 方式设置赋值了。

YYClassIvarInfo

这个类是成员变量


public方法只有一个- (instancetype)initWithIvar:(Ivar)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];

}

_offset = ivar_getOffset(ivar);

const char *typeEncoding = ivar_getTypeEncoding(ivar);

if (typeEncoding) {

_typeEncoding = [NSString stringWithUTF8String:typeEncoding];

_type = YYEncodingGetType(typeEncoding);

}

return self;

}

1 变量_ivar 保存参数ivar 

2 变量_name 保存 ivar的name

3 变量 _offset 保存 ivar 的偏移量

4 获取typeEncoding,将其保存到变量 _typeEncoding 中

5.将_typeEncoding 转换成 YYEncoding 保存到变量_type 中。


总结下YYClassMethodInfo,YYClassPropertyInfo,YYClassIvarInfo这三个类。

YYClassMethodInfo  代表该类的所有method

YYClassPropertyInfo 代表该类的所有属性

YYClassIvarInfo 代表该类的所有 成员变量

YYClassInfo 包含了自己和superClass 的所有method 属性和成员变量

这里将YYClassInfo 分析结束了。



那么我们返回<6> 出 看看这个地方具体干啥了

while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)

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

}

这里是个递归。依次调用super类,直到superClass是nil为止结束。在每次递归的过程中,对每个类的属性都进行包装成_YYModelPropertyMeta ,最后封装在allPropertyMetas 字典中,key值是 _YYModelPropertyMeta 中的name

 这里关键是生成了一个类_YYModelPropertyMeta

我们看看这个类的实现

_YYModelPropertyMeta

成员变量如下

NSString *_name; ///< property's name

YYEncodingType _type;        ///< property's type

YYEncodingNSType _nsType;    ///< property's Foundation type

BOOL _isCNumber;            ///< is c number type

Class _cls;                  ///< property's class, or nil

Class _genericCls;          ///< container's generic class, or nil if threr's no generic class

SEL _getter;                ///< getter, or nil if the instances cannot respond

SEL _setter;                ///< setter, or nil if the instances cannot respond

BOOL _isKVCCompatible;      ///< YES if it can access with key-value coding

BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver

BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:

/*

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;      ///< the key mapped to

NSArray *_mappedToKeyPath;  ///< the key path mapped to (nil if the name is not key path)

NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)

YYClassPropertyInfo *_info;  ///< property's info

_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.

这个类就一个方法实现

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

// support pseudo generic class with protocol name

if (!generic && propertyInfo.protocols) {

for (NSString *protocol in propertyInfo.protocols) {

Class cls = objc_getClass(protocol.UTF8String);

if (cls) {

generic = cls;

break;

}

}

}

_YYModelPropertyMeta *meta = [self new];

meta->_name = propertyInfo.name;

meta->_type = propertyInfo.type;

meta->_info = propertyInfo;

meta->_genericCls = generic;

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:

*/

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:

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: {

meta->_isKVCCompatible = YES;

} break;

default: break;

}

}

return meta;

}

传入的参数有 YYClassInfo, YYClassPropertyInfo,  Class 类型的三个参数

YYClassInfo 当前类

YYClassPropertyInfo  类的属性

Class 暂时不详

1 . 判断 class 和 YYClassPropertyInfo是否有协议 。对class 进行相关赋值。

2.生成meta对象,保存,property的name ,type , 和 自身,_genericCls 保存class

3.判断属性的修饰是不是id 类型的。是的话,通过函数YYClassGetNSType 给_nsType 赋值。 这个函数的返回值是枚举YYEncodingNSType 类型的。看看这个枚举值

typedef NS_ENUM (NSUInteger, YYEncodingNSType) {

YYEncodingTypeNSUnknown = 0,

YYEncodingTypeNSString,

YYEncodingTypeNSMutableString,

YYEncodingTypeNSValue,

YYEncodingTypeNSNumber,

YYEncodingTypeNSDecimalNumber,

YYEncodingTypeNSData,

YYEncodingTypeNSMutableData,

YYEncodingTypeNSDate,

YYEncodingTypeNSURL,

YYEncodingTypeNSArray,

YYEncodingTypeNSMutableArray,

YYEncodingTypeNSDictionary,

YYEncodingTypeNSMutableDictionary,

YYEncodingTypeNSSet,

YYEncodingTypeNSMutableSet,

};

这些是foundation class 类型, 包含nsstring , NSMutableString, NSValue,NSNumber,NSDecimalNumber,NSData,NSMutableData,NSDate,NSURL,NSArray,NSMutableArray,NSDictionary,NSMutableDictionary,NSSet,NSMutableSet

看下面函数怎么转换成特定类型的

/// Get the Foundation class type from property info.

static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {

if (!cls) return YYEncodingTypeNSUnknown;

if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;

if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;

if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;

if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;

if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;

if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;

if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;

if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;

if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;

if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;

if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;

if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;

if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;

if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;

if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;

return YYEncodingTypeNSUnknown;

}

纸老虎,很简单,不做介绍。一一匹配就行。

4 要是不是id 类型的就认为是数字。 给_isCNumber 变量赋值。是数字类型就YES, 反之NO

/// Whether the type is c number.

static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {

switch (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 YYEncodingTypeLongDouble: return YES;

default: return NO;

}

}

5.检查类型修饰是不是结构体类型YYEncodingTypeStruct

这里有个单例,

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;

});

枚举了foundation 中,结构体的type类型具体描述

要是在这个单例数组中包含了,type 类型,就将变量_isStructAvailableForKeyedArchiver 设置为YES

6 meta 的_cls 设置为propertyInfo 的cls(这个cls 其实就是属性前面的修饰eg:NSString)

7.要是有generic 检查modelCustomClassForDictionary 方法是否含有

8.没有检测是否_nsType 为YYEncodingTypeNSUnknown 检测方法modelCustomClassForDictionary 方法是否含有

7 和8 两步暂时不知道干啥用 的。不影响主要流程不计较

9.要是class.cls 响应getter方法,给_getter 变量赋值

10.同理给_setting 变量赋值

11 要是meta 的getter 和setting 变量赋值了。那么检测类型,标记为_isKVCCompatible 为yes 

12 返回self

所以_YYModelPropertyMeta 代表了property 。

而该类对属性数据进行了一些处理,例如getting 或者setting 方法。



接下来我们看<7>的地方

这里有个枚举customMapper 字典。看看每次枚举都干啥事情了

1.检查allPropertyMetas 字典中是否含有属性名字,没有就结束

2.要是有的话就从allPropertyMetas移除该键值对

3  枚举的键值对value的类型

4 要是字符串的话,propertyMeta 的_mappedToKey 保存该value。对mappedToKey 进行分割,分割出的propertyMeta 大于1 ,给_mappedToKeyPath 赋值keyPath 。keyPathPropertyMetas 加入propertyMeta。检查mapper 中是否含有mappedToKey,给_next 赋值。mapper[mappedToKey] 保存propertyMeta

5 要是数组的话,对数组中的每个元素进行上述4步骤

仔细看这里

if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;

[allPropertyMetas removeObjectForKey:propertyName];

[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {

propertyMeta->_mappedToKey = name;

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

mapper[name] = propertyMeta;

}];

这三个地方操作allPropertyMetas ,其实我们操作的是allPropertyMetas 里面的对象,修改了allPropertyMetas里面的对象,成员变量_allPropertyMetas 的里面的对象也就发生改变了。

我们还是做个_YYModelMeta 指针分布图更好看些





接下来我们看<5>,暂时略过<4>

- (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)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)) { 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 { CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas, CFRangeMake(0, modelMeta->_keyMappedCount), ModelSetWithPropertyMetaArrayFunction, &context); } if (modelMeta->_hasCustomTransformFromDictionary) { return [((id)self) modelCustomTransformFromDictionary:dic];

}

return YES;

}

这里有个object_getClass(self) 我们看看object_getClass(self) 和 [self class] 区别

这里有篇文章介绍

直接获取结论

1.obj为Object实例对象

结论:当obj为实例变量时,object_getClass(obj)与[obj class]输出结果一直,均获得isa指针,即指向类对象的指针。

2.obj为Class类对象

结论:当obj为类对象时,object_getClass(obj)返回类对象中的isa指针,即指向元类对象的指针;[obj class]返回的则是其本身。

3.obj为Metaclass类对象

结论:当obj为Metaclass(元类)对象时,object_getClass(obj)返回元类对象中的isa指针,因为元类对象的isa指针指向根类,所有返回的是根类对象的地址指针;[obj class]返回的则是其本身。

4.obj为Rootclass类对象

结论:当obj为Rootclass(元类)对象时,object_getClass(obj)返回根类对象中的isa指针,因为跟类对象的isa指针指向Rootclass‘s metaclass(根元类),即返回的是根元类的地址指针;[obj class]返回的则是其本身。

因为根类的isa指针其实是指向本身的,所有根元类其实就是根类,所有输出的结果是一样的。

总结:经上面初步的探索得知,object_getClass(obj)返回的是obj中的isa指针;而[obj class]则分两种情况:一是当obj为实例对象时,[obj class]中class是实例方法:- (Class)class,返回的obj对象中的isa指针;二是当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。

所以这里的self 是实例本身,

这个函数介绍

1.检查参数

2.获取self 对应的_YYModelMeta 对象

3.检查_keyMappedCount是否为0 

4.是否需要转换数据dic

5ModelSetContext 结构体声明,保存,_YYModelMeta self 还有json 字典

6 判断_keyMappedCount 是否比dic 的键值对多

7,要是_keyMappedCount 不小于dic的键值对,进行

CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);

这里有个函数指针ModelSetWithDictionaryFunction 

看看实现

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;

};

}

这个函数比较简单

1 获取_YYModelMeta

2 根据key获取 _YYModelPropertyMeta 

3 进行遍历_YYModelPropertyMeta ,调用ModelSetValueForProperty,将propertyMeta 的next指针指向自己。

这里有个_mapper 还有个_next ,是时候完全弄懂这些变量的具体含义的时候了

回到函数- (instancetype)initWithClass:(Class)cls

先看看allPropertyMetas 字典 盛放的数据都是啥

allPropertyMetas 存放的是类以及父类的所有的属性,与 属性的名字一一对应。

接下来看看mapper   先看下面这段 

[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {

propertyMeta->_mappedToKey = name;

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

mapper[name] = propertyMeta;

}];

name 是 key value 是_YYModelPropertyMeta

我们给propertyMeta 的_mappedToKey 变量赋值为name ,

propertyMeta的_next  指向是 mapper[name] 

最后mapper[name] 指向propertyMeta

这里到底是什么意思呢?

假设cls 没有实现modelCustomPropertyMapper 方法

那么 这里就可以这样认为,

我们遍历allPropertyMetas中的每一个元素,将每一个propertyMeta 的_mappedToKey 赋值为name

因为这时候mapper什么元素也没有,即使有,也没有包含name 的键值对。那么。

propertyMeta->_next = mapper[name] ?: nil;相当于propertyMeta->_next = nil

最后, 我们mapper[name] = propertyMeta;

见下面

propertyMeta->_mappedToKey = name;

propertyMeta->_next =nil;

mapper[name] = propertyMeta;

根据这里看,我们知道了,mapper 和allPropertyMetas 差不多,都是存了所有属性的键值对。

那为什么会有propertyMeta->_next = mapper[name] ?: nil; 呢

就要看modelCustomPropertyMapper 这里面的实现了

我们知道modelCustomPropertyMapper 返回的是一个Dic

分步看这块代码

1.首先从allPropertyMetas 数组找key对应的_YYModelPropertyMeta ,没有就返回了。不用解释。

2.要是有的话,就从allPropertyMetas 中移除该数组。那么在上面枚举allPropertyMetas 该类的时候就不包含该key 了。那么从数组移除的这个键值对要做啥处理呢。

3.这里先处理value。检测value 字符串还是数组

4.value是字符串,我们把_YYModelPropertyMeta 的_mappedToKey 设置为value,而在allPropertyMetas 设置的是key。 看到这里大概明白了,key值替换。

5. 这里对value 进行 点分割。其实就是判断是否需要KVC,要是有.的话,那么我们就用

_mappedToKeyPath 保存住分割后的数组。这里有propertyMeta->_next = mapper[mappedToKey] ?: nil; 什么时候出现mapper[mappedToKey] 不是nil的情况呢?

我们看mappedToKey 是代表的value 。我们知道在一个字典里面,key 一定是唯一的,不可能出现重复的,而value 可以出现重复的。

假设这里modelCustomPropertyMapper 函数返回的字典是@{"userid":"name","id":"name"}  那么在遍历customMapper 第二次的时候就会出现mapper[mappedToKey] 不是nil的情况,她保存的是上一次 userid 对应的_YYModelPropertyMeta,因为我们将第二次的_YYModelPropertyMeta 的_next 保存userid 对应的 _YYModelPropertyMeta 

见图

6.value 是数组,方式很相同,不做解释

allPropertyMetas 枚举的时候 有这么一段,propertyMeta->_next = mapper[name] ?: nil;

类似链表的操作。因为name 可能和modelCustomPropertyMapper 方法中保存的key 值有重复。

我们返回ModelSetWithDictionaryFunction 函数中

这里的mapper 盛放的是 每个属性name 对应的自己的_YYModelPropertyMeta键值对


篇幅所限,我们在下面的章节中分析ModelSetValueForProperty 函数

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容