終于把前面的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 函數