YYKit 源碼解析(5)-YYModel

終于把前面的base文件夾簡(jiǎn)簡(jiǎn)單單的看了一遍副瀑,終于可以回到正片上來(lái)了弓熏,保證不爛尾。

項(xiàng)目天天用yymodel解析數(shù)據(jù)俗扇,效率高也沒(méi)看看到底為啥效率高硝烂,最近有時(shí)間,仔細(xì)看看铜幽。

NSObject+YYModel

YYClassInfo

這個(gè)類怎么學(xué)習(xí)呢滞谢?我從調(diào)用入口看是看起。

由于yykit 框架里面的yymodel 方法名沒(méi)有標(biāo)記yy_ 所以除抛,我們使用的是yymodel 進(jìn)行分析狮杨。

yymodel 類是NSObject 的category?

+ (instancetype)yy_modelWithJSON:(id)json {

NSDictionary *dic = [self _yy_dictionaryWithJSON:json];

return [self yy_modelWithDictionary:dic];

}


1.調(diào)用?+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json<1> 函數(shù)獲取 dic

2 調(diào)用+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary<2> 返回結(jié)果



我們看<1> 的函數(shù)

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

}

我們從這里看出來(lái)我們外界傳入到該類的參數(shù)可以是NSDictionary?NSString?NSData

但是最后返回的結(jié)果是NSDictionary?



接下來(lái)我們看<2>出的函數(shù)

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

}

這個(gè)函數(shù)算是真正解析的入口了。我們看看這個(gè)方法都干了啥事情

1 第一步校驗(yàn)參數(shù)是否正確

2獲取該類的class

3 通過(guò)_YYModelMeta 的類方法+ (instancetype)metaWithClass:(Class)cls <3>獲取一個(gè)_YYModelMeta 對(duì)象

4 判斷 _YYModelMeta對(duì)象是否 有_hasCustomClassFromDictionary標(biāo)記為YES到忽,有的話橄教,cls 就調(diào)用下+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary <4>?方法 .要是返會(huì)cls 那么就給賦值,沒(méi)有則用原來(lái)的cls

5 .創(chuàng)建對(duì)象

6 調(diào)用- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic <5>方法

7返回

這個(gè)函數(shù)有個(gè)新類_YYModelMeta

我們看看這個(gè)類干啥的

先看看這個(gè)類的屬性

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;

屬性先不做介紹喘漏,看源碼實(shí)現(xiàn)护蝶。先調(diào)用這個(gè)函數(shù)


這里是<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;

}

這個(gè)函數(shù)干啥了呢?

1.生成一個(gè)單例緩存CFMutableDictionaryRef cache 和一個(gè)信號(hào)量

2.從緩存中查找key 是class的?_YYModelMeta

3.要是緩存中沒(méi)有meta 或者meta 標(biāo)記為需要更新

4. 通過(guò)- (instancetype)initWithClass:(Class)cls 方法創(chuàng)建meta?

5 將meta 更新到單例緩存數(shù)組中

總結(jié)下這個(gè)函數(shù),這個(gè)函數(shù)就是從緩存獲取解析完畢的_YYModelMeta 沒(méi)有就解析該類

接下來(lái)看- (instancetype)initWithClass:(Class)cls 函數(shù)

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

}

這個(gè)方法我們看看都干啥了

1. 生成一個(gè)YYClassInfo 對(duì)象

2.要是 cla 實(shí)現(xiàn)?+ (nullable NSArray*)modelPropertyBlacklist 方法了 翩迈,回去改方法的數(shù)據(jù)

3.要是cls 實(shí)現(xiàn)+ (nullable NSArray*)modelPropertyWhitelist; 方法页响,臨時(shí)變量保存改方法的返回值

4 要是cls 實(shí)現(xiàn)+ (NSDictionary *)modelContainerPropertyGenericClass 方法 创译。那么就對(duì)返回的數(shù)據(jù) 進(jìn)行簡(jiǎn)單的處理荠锭,保存到臨時(shí)變量中蜜暑。(這里適配了 obj 是class 或者字符串兩種形式。)

5.給臨時(shí)數(shù)組allPropertyMetas 增加key是_YYModelPropertyMeta 的name 值是self 對(duì)象返十。這里的具體細(xì)節(jié)解析在下面分析妥泉,暫時(shí)標(biāo)記為<6>

6. 變量_allPropertyMetas 指向臨時(shí)變量allPropertyMetas 的所有value 值

7.要是cls實(shí)現(xiàn)了?+ (NSDictionary *)modelCustomPropertyMapper ,那么調(diào)用cls調(diào)用+ (NSDictionary *)modelCustomPropertyMapper 方法洞坑。給臨時(shí)變量mapper盲链,keyPathPropertyMetas ?,multiKeysPropertyMetas 賦值迟杂。具體賦值看<7>出

8.修改臨時(shí)變量allPropertyMetas 中的_YYModelPropertyMeta 相關(guān)屬性

9.給變量_mapper匈仗,_keyPathPropertyMetas,_multiKeysPropertyMetas 賦值逢慌。

10給變量?_classInfo?_keyMappedCount ?_nsType 賦值。這里有個(gè)static force_inline YYEncodingNSType YYClassGetNSType(Class cls)<8> 方法间狂。

11 給變量?_hasCustomWillTransformFromDictionary?_hasCustomTransformFromDictionary?_hasCustomTransformToDictionary?_hasCustomClassFromDictionary 賦值 這四個(gè)參數(shù)分別標(biāo)記是否有一個(gè)特定關(guān)聯(lián)的函數(shù)

12 返回self

這個(gè)類關(guān)鍵處?YYClassInfo ?_YYModelPropertyMeta ?和 沒(méi)有分析的標(biāo)記的<6>出的地方


接下來(lái)先看?YYClassInfo ?類

分析類成員變量


黃色代表 public 屬性

粉紅色代表 privite 變量

接下來(lái)我們看初始化

public方法提供了兩個(gè)攻泼,只有類方法初始化,沒(méi)有實(shí)例初始化

+ (instancetype)classInfoWithClassName:(NSString *)className

+ (instancetype)classInfoWithClass:(Class)cls

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

Class cls = NSClassFromString(className);

return [self classInfoWithClass:cls];

}

這個(gè)函數(shù)就是將className 轉(zhuǎn)換成class 再調(diào)用上面的初始化方法

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

}

這個(gè)函數(shù)干啥了呢

1.單例實(shí)例化一個(gè)classCache 字典 和metaCache 字典,還初始化了信號(hào)量

2. 檢查class 是meta 還是 class 忙菠,是meta 從meta 字典去key 是cls 的值何鸡,不是meta ,那么從class 字典中取 key是cls的值牛欢。

3 要是從緩存中獲取到了cls 值骡男,并且cls標(biāo)記為 需要更新,那么調(diào)用?- (void)_update <9>

4沒(méi)有值傍睹,那么創(chuàng)建一個(gè)YYClassInfo 隔盛,并根據(jù)是否是meta保存到相應(yīng)字典中去

5返回。

這個(gè)函數(shù)就是從緩存中獲取class 信息拾稳,沒(méi)有就創(chuàng)建一個(gè)吮炕,具體創(chuàng)建方法是這個(gè)房- (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;

}

這個(gè)方法干啥事情了呢访得?

1. 根據(jù)類的特性 給 變量_cls?_superCls?_isMeta?_metaCls?_name 賦值龙亲,

2.調(diào)用- (void)_update 方法

3.解析父類class ,將結(jié)果賦值給?_superClassInfo 結(jié)構(gòu)如下



className 是解析的class

superClassInfo 保存的是super的解析的YYClassInfo?

類結(jié)構(gòu)明白了悍抑。那么我們應(yīng)該看看 <9>?- (void)_update ?這個(gè)類解析啥了

- (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 的每一個(gè)方法鳄炉,將YYClassMethodInfo 保存到到變量_methodInfos字典中,key是?YYClassMethodInfo 的name 搜骡,將字典

3.獲取objc_property_t 列表拂盯,用YYClassPropertyInfo 解析每一個(gè)屬性,將YYClassPropertyInfo 保存到變量_propertyInfos 中key 是YYClassPropertyInfo 的name

4同理浆兰,_ivarInfos 保存該類的Ivar 磕仅,將Ivar 封裝在了YYClassIvarInfo,key是name

5.將_needUpdate 變量設(shè)置為NO

這個(gè)類里面出現(xiàn)了三個(gè)新的類YYClassMethodInfo簸呈,YYClassPropertyInfo榕订,YYClassIvarInfo。

YYClassMethodInfo類


這個(gè)類的方法就一個(gè)

- (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 的 方法實(shí)現(xiàn)

4 獲取 變量_sel的name 將其保存在 變量_name 屬性中

5 獲取method 的編碼蜕便,保存在變量_typeEncoding 中

6 獲取method 的返回值類型劫恒,保存在變量_returnTypeEncoding 中

7獲取mthod的參數(shù)數(shù)量,變量獲取每一個(gè)參數(shù)類型轿腺,將參數(shù)類型保存依次加入到參數(shù)數(shù)組中两嘴,讓變量_argumentTypeEncodings 引用該參數(shù)數(shù)組。

8.返回self

YYClassMethodInfo 類就是解析了Method 的方方面面族壳。

YYClassPropertyInfo


上圖是YYClassPropertyInfo 的類結(jié)構(gòu)

方法也就一個(gè)

- (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 結(jié)構(gòu)體

2獲取?objc_property_t 的name 保存到 ?變量_name 中

3獲取objc_property_t 結(jié)構(gòu)體的 屬性和數(shù)量

這里我們就應(yīng)該了解屬性的type 蘋(píng)果官網(wǎng)的編碼? 還有一篇

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:


截圖如下

這個(gè)表里面沒(méi)有T T代表屬性類型憔辫。

eg

@property(readonly) int intReadonly;

結(jié)果是

Ti,R,VintReadonly ? ?Ti 代表int 類型,R 代表只讀屬性仿荆,V代表實(shí)例變量intReadonly

4.解析屬性贰您,給相關(guān)變量賦值?

5.將解析的type 賦值給_type 變量

6.要是沒(méi)有設(shè)置get set 方法坏平。設(shè)置get set 方法。?

7 返回self

這個(gè)函數(shù)的關(guān)鍵部分是上面介紹的第四步

這里分情況討論objc_property_attribute_t 的name[0] 屬性值

<1> name[0]=T

這里給_typeEncoding 變量賦值锦亦,值是objc_property_attribute_t ?value 的字符串舶替。

這里有個(gè)編碼?YYEncodingType YYEncodingGetType(const char *typeEncoding)

返回結(jié)果是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 編碼區(qū)對(duì)應(yīng)的是 類型編碼 官網(wǎng)?

YYEncodingTypeQualifierMask 編碼區(qū)對(duì)應(yīng)的是 方法編碼官網(wǎng)?最下方

YYEncodingTypePropertyMask 對(duì)應(yīng)的屬性編碼 官網(wǎng)

接下來(lái)看字符串怎么轉(zhuǎn)換的成編碼的

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檢測(cè)參數(shù)是否正確

2 判斷字符串是否是YYEncodingTypeQualifierMask 。給type 賦值

3 檢測(cè) 字符串是否是YYEncodingTypeMask 給type賦值

這里沒(méi)有對(duì)YYEncodingTypePropertyMask 區(qū)間進(jìn)行檢測(cè)


@property (assign) NSString * str

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

這里 type 獲取的是 NSString 的類型是 id

接下來(lái)杠园,我們判斷type 類型是不是id 類型的顾瞪,是id 類型。用_typeEncoding 變量類型進(jìn)一步校驗(yàn)抛蚁。

@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

剩余的部分是協(xié)議部分.每一個(gè)協(xié)議都是用<> 包含起來(lái)的孵延。

由于協(xié)議可以多個(gè),所以將其用數(shù)組承接亲配。放入變量?_protocols 中

<2>name[0]=V

V 是成員變量的代表 value 的值成員變量的名字

<3> 其他的就是onlyRead 尘应,nonatomic,Getting,Setting 等的特有的標(biāo)示符號(hào)了吼虎。

這里get方法和setting 設(shè)置type 的時(shí)候同時(shí)給?_getter 和_setter 方式設(shè)置賦值了犬钢。

YYClassIvarInfo

這個(gè)類是成員變量


public方法只有一個(gè)- (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 保存參數(shù)ivar?

2 變量_name 保存 ivar的name

3 變量?_offset 保存 ivar 的偏移量

4 獲取typeEncoding,將其保存到變量?_typeEncoding 中

5.將_typeEncoding 轉(zhuǎn)換成 YYEncoding 保存到變量_type 中思灰。


總結(jié)下YYClassMethodInfo玷犹,YYClassPropertyInfo,YYClassIvarInfo這三個(gè)類洒疚。

YYClassMethodInfo ?代表該類的所有method

YYClassPropertyInfo 代表該類的所有屬性

YYClassIvarInfo 代表該類的所有 成員變量

YYClassInfo 包含了自己和superClass 的所有method 屬性和成員變量

這里將YYClassInfo 分析結(jié)束了撤奸。



那么我們返回<6> 出 看看這個(gè)地方具體干啥了

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;

}

這里是個(gè)遞歸劫拢。依次調(diào)用super類熟吏,直到superClass是nil為止結(jié)束。在每次遞歸的過(guò)程中势腮,對(duì)每個(gè)類的屬性都進(jìn)行包裝成_YYModelPropertyMeta 吗浩,最后封裝在allPropertyMetas 字典中岁钓,key值是?_YYModelPropertyMeta 中的name

?這里關(guān)鍵是生成了一個(gè)類_YYModelPropertyMeta

我們看看這個(gè)類的實(shí)現(xiàn)

_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.

這個(gè)類就一個(gè)方法實(shí)現(xiàn)

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

}

傳入的參數(shù)有?YYClassInfo,?YYClassPropertyInfo啊央, ?Class 類型的三個(gè)參數(shù)

YYClassInfo 當(dāng)前類

YYClassPropertyInfo ?類的屬性

Class 暫時(shí)不詳

1 . 判斷 class 和?YYClassPropertyInfo是否有協(xié)議 乓土。對(duì)class 進(jìn)行相關(guān)賦值宪潮。

2.生成meta對(duì)象,保存趣苏,property的name 狡相,type , 和 自身食磕,_genericCls 保存class

3.判斷屬性的修飾是不是id 類型的尽棕。是的話,通過(guò)函數(shù)YYClassGetNSType?給_nsType 賦值彬伦。 這個(gè)函數(shù)的返回值是枚舉YYEncodingNSType 類型的滔悉。看看這個(gè)枚舉值

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

看下面函數(shù)怎么轉(zhuǎn)換成特定類型的

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

}

紙老虎,很簡(jiǎn)單询张,不做介紹孙乖。一一匹配就行。

4 要是不是id 類型的就認(rèn)為是數(shù)字份氧。 給_isCNumber 變量賦值唯袄。是數(shù)字類型就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.檢查類型修飾是不是結(jié)構(gòu)體類型YYEncodingTypeStruct

這里有個(gè)單例,

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 中蜗帜,結(jié)構(gòu)體的type類型具體描述

要是在這個(gè)單例數(shù)組中包含了恋拷,type 類型,就將變量_isStructAvailableForKeyedArchiver 設(shè)置為YES

6 meta 的_cls 設(shè)置為propertyInfo 的cls(這個(gè)cls 其實(shí)就是屬性前面的修飾eg:NSString)

7.要是有g(shù)eneric 檢查modelCustomClassForDictionary 方法是否含有

8.沒(méi)有檢測(cè)是否_nsType 為YYEncodingTypeNSUnknown 檢測(cè)方法modelCustomClassForDictionary 方法是否含有

7 和8 兩步暫時(shí)不知道干啥用 的厅缺。不影響主要流程不計(jì)較

9.要是class.cls 響應(yīng)getter方法蔬顾,給_getter 變量賦值

10.同理給_setting 變量賦值

11 要是meta 的getter 和setting 變量賦值了。那么檢測(cè)類型湘捎,標(biāo)記為_(kāi)isKVCCompatible 為yes?

12 返回self

所以_YYModelPropertyMeta 代表了property 诀豁。

而該類對(duì)屬性數(shù)據(jù)進(jìn)行了一些處理,例如getting 或者setting 方法窥妇。



接下來(lái)我們看<7>的地方

這里有個(gè)枚舉customMapper 字典舷胜。看看每次枚舉都干啥事情了

1.檢查allPropertyMetas 字典中是否含有屬性名字活翩,沒(méi)有就結(jié)束

2.要是有的話就從allPropertyMetas移除該鍵值對(duì)

3 ?枚舉的鍵值對(duì)value的類型

4 要是字符串的話烹骨,propertyMeta 的_mappedToKey 保存該value翻伺。對(duì)mappedToKey 進(jìn)行分割,分割出的propertyMeta 大于1 沮焕,給_mappedToKeyPath 賦值keyPath 吨岭。keyPathPropertyMetas 加入propertyMeta窥翩。檢查mapper 中是否含有mappedToKey沫换,給_next 賦值。mapper[mappedToKey] 保存propertyMeta

5 要是數(shù)組的話夸赫,對(duì)數(shù)組中的每個(gè)元素進(jìn)行上述4步驟

仔細(xì)看這里

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;

}];

這三個(gè)地方操作allPropertyMetas 空入,其實(shí)我們操作的是allPropertyMetas 里面的對(duì)象,修改了allPropertyMetas里面的對(duì)象族檬,成員變量_allPropertyMetas 的里面的對(duì)象也就發(fā)生改變了歪赢。

我們還是做個(gè)_YYModelMeta 指針?lè)植紙D更好看些





接下來(lái)我們看<5>,暫時(shí)略過(guò)<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;

}

這里有個(gè)object_getClass(self) 我們看看object_getClass(self) 和 [self class] 區(qū)別

這里有篇文章介紹

直接獲取結(jié)論

1.obj為Object實(shí)例對(duì)象

結(jié)論:當(dāng)obj為實(shí)例變量時(shí),object_getClass(obj)與[obj class]輸出結(jié)果一直单料,均獲得isa指針埋凯,即指向類對(duì)象的指針。

2.obj為Class類對(duì)象

結(jié)論:當(dāng)obj為類對(duì)象時(shí)扫尖,object_getClass(obj)返回類對(duì)象中的isa指針白对,即指向元類對(duì)象的指針;[obj class]返回的則是其本身换怖。

3.obj為Metaclass類對(duì)象

結(jié)論:當(dāng)obj為Metaclass(元類)對(duì)象時(shí)甩恼,object_getClass(obj)返回元類對(duì)象中的isa指針,因?yàn)樵悓?duì)象的isa指針指向根類沉颂,所有返回的是根類對(duì)象的地址指針条摸;[obj class]返回的則是其本身。

4.obj為Rootclass類對(duì)象

結(jié)論:當(dāng)obj為Rootclass(元類)對(duì)象時(shí)铸屉,object_getClass(obj)返回根類對(duì)象中的isa指針钉蒲,因?yàn)楦悓?duì)象的isa指針指向Rootclass‘s metaclass(根元類),即返回的是根元類的地址指針彻坛;[obj class]返回的則是其本身顷啼。

因?yàn)楦惖膇sa指針其實(shí)是指向本身的,所有根元類其實(shí)就是根類昌屉,所有輸出的結(jié)果是一樣的钙蒙。

總結(jié):經(jīng)上面初步的探索得知,object_getClass(obj)返回的是obj中的isa指針怠益;而[obj class]則分兩種情況:一是當(dāng)obj為實(shí)例對(duì)象時(shí)仪搔,[obj class]中class是實(shí)例方法:- (Class)class,返回的obj對(duì)象中的isa指針蜻牢;二是當(dāng)obj為類對(duì)象(包括元類和根類以及根元類)時(shí)烤咧,調(diào)用的是類方法:+ (Class)class偏陪,返回的結(jié)果為其本身。

所以這里的self 是實(shí)例本身煮嫌,

這個(gè)函數(shù)介紹

1.檢查參數(shù)

2.獲取self 對(duì)應(yīng)的_YYModelMeta 對(duì)象

3.檢查_(kāi)keyMappedCount是否為0?

4.是否需要轉(zhuǎn)換數(shù)據(jù)dic

5ModelSetContext 結(jié)構(gòu)體聲明笛谦,保存,_YYModelMeta self 還有json 字典

6 判斷_keyMappedCount 是否比dic 的鍵值對(duì)多

7昌阿,要是_keyMappedCount 不小于dic的鍵值對(duì)饥脑,進(jìn)行

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

這里有個(gè)函數(shù)指針ModelSetWithDictionaryFunction?

看看實(shí)現(xiàn)

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;

};

}

這個(gè)函數(shù)比較簡(jiǎn)單

1 獲取_YYModelMeta

2 根據(jù)key獲取?_YYModelPropertyMeta?

3 進(jìn)行遍歷_YYModelPropertyMeta ,調(diào)用ModelSetValueForProperty懦冰,將propertyMeta 的next指針指向自己灶轰。

這里有個(gè)_mapper 還有個(gè)_next ,是時(shí)候完全弄懂這些變量的具體含義的時(shí)候了

回到函數(shù)- (instancetype)initWithClass:(Class)cls

先看看allPropertyMetas 字典 盛放的數(shù)據(jù)都是啥

allPropertyMetas 存放的是類以及父類的所有的屬性刷钢,與 屬性的名字一一對(duì)應(yīng)笋颤。

接下來(lái)看看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

這里到底是什么意思呢内地?

假設(shè)cls 沒(méi)有實(shí)現(xiàn)modelCustomPropertyMapper 方法

那么 這里就可以這樣認(rèn)為伴澄,

我們遍歷allPropertyMetas中的每一個(gè)元素,將每一個(gè)propertyMeta 的_mappedToKey 賦值為name

因?yàn)檫@時(shí)候mapper什么元素也沒(méi)有阱缓,即使有非凌,也沒(méi)有包含name 的鍵值對(duì)。那么荆针。

propertyMeta->_next = mapper[name] ?: nil;相當(dāng)于propertyMeta->_next = nil

最后敞嗡, 我們mapper[name] = propertyMeta;

見(jiàn)下面

propertyMeta->_mappedToKey = name;

propertyMeta->_next =nil;

mapper[name] = propertyMeta;

根據(jù)這里看,我們知道了祭犯,mapper 和allPropertyMetas 差不多秸妥,都是存了所有屬性的鍵值對(duì)。

那為什么會(huì)有propertyMeta->_next = mapper[name] ?: nil; 呢

就要看modelCustomPropertyMapper 這里面的實(shí)現(xiàn)了

我們知道m(xù)odelCustomPropertyMapper 返回的是一個(gè)Dic

分步看這塊代碼

1.首先從allPropertyMetas 數(shù)組找key對(duì)應(yīng)的_YYModelPropertyMeta 沃粗,沒(méi)有就返回了粥惧。不用解釋。

2.要是有的話最盅,就從allPropertyMetas 中移除該數(shù)組突雪。那么在上面枚舉allPropertyMetas 該類的時(shí)候就不包含該key 了。那么從數(shù)組移除的這個(gè)鍵值對(duì)要做啥處理呢涡贱。

3.這里先處理value咏删。檢測(cè)value 字符串還是數(shù)組

4.value是字符串,我們把_YYModelPropertyMeta 的_mappedToKey 設(shè)置為value问词,而在allPropertyMetas 設(shè)置的是key督函。 看到這里大概明白了,key值替換。

5. 這里對(duì)value 進(jìn)行 點(diǎn)分割辰狡。其實(shí)就是判斷是否需要KVC锋叨,要是有.的話,那么我們就用

_mappedToKeyPath 保存住分割后的數(shù)組宛篇。這里有propertyMeta->_next = mapper[mappedToKey] ?: nil; 什么時(shí)候出現(xiàn)mapper[mappedToKey] 不是nil的情況呢?

我們看mappedToKey 是代表的value 。我們知道在一個(gè)字典里面叫倍,key 一定是唯一的偷卧,不可能出現(xiàn)重復(fù)的,而value 可以出現(xiàn)重復(fù)的吆倦。

假設(shè)這里modelCustomPropertyMapper 函數(shù)返回的字典是@{"userid":"name","id":"name"} ?那么在遍歷customMapper 第二次的時(shí)候就會(huì)出現(xiàn)mapper[mappedToKey] 不是nil的情況听诸,她保存的是上一次?userid 對(duì)應(yīng)的_YYModelPropertyMeta,因?yàn)槲覀儗⒌诙蔚腳YYModelPropertyMeta 的_next 保存userid 對(duì)應(yīng)的?_YYModelPropertyMeta?

見(jiàn)圖

6.value 是數(shù)組蚕泽,方式很相同蛇更,不做解釋

allPropertyMetas 枚舉的時(shí)候 有這么一段,propertyMeta->_next = mapper[name] ?: nil;

類似鏈表的操作赛糟。因?yàn)閚ame 可能和modelCustomPropertyMapper 方法中保存的key 值有重復(fù)。

我們返回ModelSetWithDictionaryFunction 函數(shù)中

這里的mapper 盛放的是 每個(gè)屬性name 對(duì)應(yīng)的自己的_YYModelPropertyMeta鍵值對(duì)


篇幅所限砸逊,我們?cè)谙旅娴恼鹿?jié)中分析ModelSetValueForProperty 函數(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末璧南,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子师逸,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊崭,死亡現(xiàn)場(chǎng)離奇詭異根时,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)胸遇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)峰搪,“玉大人,你說(shuō)我怎么就攤上這事〖坡荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵呐萌,是天一觀的道長(zhǎng)谊娇。 經(jīng)常有香客問(wèn)我罗晕,道長(zhǎng),這世上最難降的妖魔是什么莽囤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任谬擦,我火速辦了婚禮,結(jié)果婚禮上朽缎,老公的妹妹穿的比我還像新娘惨远。我一直安慰自己,他們只是感情好话肖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布北秽。 她就那樣靜靜地躺著,像睡著了一般最筒。 火紅的嫁衣襯著肌膚如雪羡儿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天是钥,我揣著相機(jī)與錄音,去河邊找鬼缅叠。 笑死悄泥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肤粱。 我是一名探鬼主播弹囚,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼领曼!你這毒婦竟也來(lái)了鸥鹉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庶骄,失蹤者是張志新(化名)和其女友劉穎毁渗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體单刁,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灸异,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肺樟。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡檐春,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出么伯,到底是詐尸還是另有隱情疟暖,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布田柔,位于F島的核電站俐巴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凯楔。R本人自食惡果不足惜窜骄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摆屯。 院中可真熱鬧邻遏,春花似錦、人聲如沸虐骑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)廷没。三九已至丰榴,卻和暖如春吆你,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工觉吭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枫夺。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓鹦马,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親过椎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子室梅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容