前言
本文的中文注釋代碼demo更新在我的github上相嵌。
上篇 YYModel源代碼分析(二)YYClassInfo
主要分析了YYClassInfo文件蹋盆。本篇會主要集中在NSObject+YYModel文件上。文章內容會包含一些與JSONModel的比較那先,想了解JSONModel,可以參考JSONModel源代碼解析赡艰。
主體分層
NSObject+YYModel主要分為以下幾個部分:
- 內部使用的C函數(shù)部分
@interface _YYModelPropertyMeta : NSObject
@interface _YYModelMeta : NSObject
@interface NSObject (YYModel) : NSObject
@interface NSArray (YYModel) : NSObject
@interface NSDictionary (YYModel) : NSObject
-
@protocol YYModel <NSObject>
:接口YYModel
由于代碼較多售淡,所以會挑重點的部分進行介紹。
NSObject+YYModel源代碼
@interface _YYModelPropertyMeta : NSObject
聲明
/// A property info in object model.
// model property的進一步分裝
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name //property名
YYEncodingType _type; ///< property's type //property的encode解析值
YYEncodingNSType _nsType; ///< property's Foundation type //property的foundation類型
BOOL _isCNumber; ///< is c number type //是不是c語言的數(shù)字
Class _cls; ///< property's class, or nil //property的class
Class _genericCls; ///< container's generic class, or nil if threr's no generic class //property內包含的類class
SEL _getter; ///< getter, or nil if the instances cannot respond //property getter方法
SEL _setter; ///< setter, or nil if the instances cannot respond //property setter方法
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding //是否可以使用KVC
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver //是否是struct并且可以archiver/unarchiver
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary: //是否包含本本地的class轉換
/*
property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; ///< the key mapped to //property本地的key mapper的key
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path) //property本地的key mapper的key path列表
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys) /property本地的key mapper的key列表
YYClassPropertyInfo *_info; ///< property's info //property的YYClassPropertyInfo info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key. //同個key的多個property的映射next指針
}
@end
其中需要理解一下的包括:
1._hasCustomClassFromDictionary
就是判斷是否有本地的不同class的映射
如下例子慷垮,當dictionary包含不同的值的時候揖闸,映射的model類型不同
@implementation YYBaseUser
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
if (dictionary[@"localName"]) {
return [YYLocalUser class];
} else if (dictionary[@"remoteName"]) {
return [YYRemoteUser class];
}
return [YYBaseUser class];
}
2.keyMapper相關內容
如下例子,dictionary中不同的key料身,對應的model的property有一個映射關系
其中對于包含"."的映射汤纸,則是一種多層的映射關系
+ (NSDictionary *)modelCustomPropertyMapper {
return @{ @"name" : @"n",
@"count" : @"ext.c",
@"desc1" : @"ext.d", // mapped to same key path
@"desc2" : @"ext.d", // mapped to same key path
@"desc3" : @"ext.d.e",
@"desc4" : @".ext",
@"modelID" : @[@"ID", @"Id", @"id", @"ext.id"]};
}
3._YYModelPropertyMeta *_next
同一個key的下一個meta解析類型
因為有了key mapper,所以會出現(xiàn)同一個mapper的key值芹血,可能會一對多個model的property值贮泞,這邊設計了一個_next指針進行鏈接
4.Class _genericCls
包含的類型
如下例子,property會有NSArray\NSDictionary\NSSet類型幔烛,內部的類型就通過該參數(shù)表示
@interface YYTestCustomClassModel : NSObject
@property (nonatomic, strong) NSArray *users;
@property (nonatomic, strong) NSDictionary *userDict;
@property (nonatomic, strong) NSSet *userSet;
@end
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"users" : YYBaseUser.class,
@"userDict" : YYBaseUser.class,
@"userSet" : YYBaseUser.class};
}
實現(xiàn)
//通過YYClassInfo啃擦,YYClassPropertyInfo,Class對象解析成_YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// support pseudo generic class with protocol name
// 這邊也考慮了饿悬,某些類型寫在protocol中令蛉,包含在<>中,和JSONModel一樣進行一下protocol的類型判斷
// 比如NSArray<TestObject> xxx
if (!generic && propertyInfo.protocols) {
for (NSString *protocol in propertyInfo.protocols) {
Class cls = objc_getClass(protocol.UTF8String);
if (cls) {
generic = cls;
break;
}
}
}
//構造_YYModelPropertyMeta對象
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name; //設置名
meta->_type = propertyInfo.type; //設置encode type
meta->_info = propertyInfo; //設置YYClassPropertyInfo
meta->_genericCls = generic; //設置可能有的包含類
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { //如果是OC對象狡恬,則判斷是不是標準oc foundation類型
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
} else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type); //判斷是否是c 數(shù)字
}
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) { //判斷和是否是struct
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
//NSKeyedUnarchiver只可以解析以下的struct
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) { //如果是其它oc類型,用類型去判斷是否需要類型轉換
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}
//設置getter方法
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
//設置setter方法
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}
//如果包含getter和setter方法
if (meta->_getter && meta->_setter) {
/*
KVC invalid type:
long double
pointer (such as SEL/CoreFoundation object)
*/
//以下代表可以使用kvc進行附值
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
實現(xiàn)部分還是容易理解弟劲,主要還是通過YYClassInfo运杭,YYClassPropertyInfo,Class對象解析成_YYModelPropertyMeta函卒。
@interface _YYModelMeta : NSObject
聲明
/// A class info in object model.
// model class的進一層封裝
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo; //YYClassInfo類型
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper; //key mapper的值辆憔,key是mapped key and key path撇眯,Value是_YYModelPropertyMeta類型
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas; //所有的_YYModelPropertyMeta列表
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas; //所有的路徑映射property緩存
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas; //所有的多層映射property緩存
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount; //key mapper數(shù)量
/// Model class type.
YYEncodingNSType _nsType; //model class的oc類型
BOOL _hasCustomWillTransformFromDictionary; //是否包含本地的某些值進行替換
BOOL _hasCustomTransformFromDictionary; //是否有本地的值的類型判斷
BOOL _hasCustomTransformToDictionary; //與上個相反,當轉成dictionary的時候虱咧,轉換的方式
BOOL _hasCustomClassFromDictionary; //是否有本地的類型的轉換
}
@end
其中熊榛,需要理解的包括:
NSDictionary *_mapper;
NSArray *_allPropertyMetas;
NSArray *_keyPathPropertyMetas;
NSArray *_multiKeysPropertyMetas;
_mapper是包括了所有class和superclass的property的key value緩存,其中的key是所有已經映射過key name和不需要映射的property name組成腕巡。
_allPropertyMetas是所有class和superclass的property解析_YYModelPropertyMeta列表玄坦。
_keyPathPropertyMetas是表示映射如果是一個路徑比如@"count" : @"ext.c"
下的_YYModelPropertyMeta列表。
_multiKeysPropertyMetas是表示映射如果是一個NSArray比如@"modelID" : @[@"ID", @"Id", @"id", @"ext.id"]
下的_YYModelPropertyMeta列表绘沉。
BOOL _hasCustomWillTransformFromDictionary; //是否包含本地的某些值進行替換
BOOL _hasCustomTransformFromDictionary; //是否有本地的值的類型判斷
BOOL _hasCustomTransformToDictionary; //與上個相反煎楣,當轉成dictionary的時候,轉換的方式
BOOL _hasCustomClassFromDictionary; //是否有本地的類型的轉換
判斷方式:
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
對應的實際例子:
@interface YYTestCustomTransformModel : NSObject
@property uint64_t id;
@property NSString *content;
@property NSDate *time;
@end
-(NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic{
if (dic) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:dic];
if (dict[@"date"]) {
dict[@"time"] = dict[@"date"];
}
return dict;
}
return dic;
}
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
NSNumber *time = dic[@"time"];
if ([time isKindOfClass:[NSNumber class]] && time.unsignedLongLongValue != 0) {
_time = [NSDate dateWithTimeIntervalSince1970:time.unsignedLongLongValue / 1000.0];
return YES;
} else {
return NO;
}
}
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
if (_time) {
dic[@"time"] = @((uint64_t)(_time.timeIntervalSince1970 * 1000));
return YES;
} else {
return NO;
}
}
最后一個在_YYModelPropertyMeta
已經提過车伞,這里不再提择懂。
實現(xiàn)
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]; //構造 YYClassInfo結構體
if (!classInfo) return nil;
self = [super init];
// Get black list
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) { //黑名單,不解析的property
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// Get white list
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) { //白名單另玖,只解析的property
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// Get container property's generic class
//獲得包含的類的類型
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass]; //獲得包含類映射的dictionary
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { //遍歷dictionary
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj); //獲得映射的類型
if (!meta) return; //不是類型返回
if (class_isMetaClass(meta)) { //如果是meta class的話,直接設置(因為是對象的類類型)
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) { //如果是string的話困曙,轉成class,設置
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp; //獲得包含類的映射dictionary
}
}
// Create all property metas.
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
//遞歸class和superclass谦去,但忽略nsobject/nsproxy
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
//獲得class的所有YYClassPropertyInfo緩存
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
if (!propertyInfo.name) continue;
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue; //跳過黑名單
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue; //只判斷拍名單
//構造_YYModelPropertyMeta結構體
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue; //沒有名字跳過
if (!meta->_getter || !meta->_setter) continue; //沒有getter或者setter方法跳過
if (allPropertyMetas[meta->_name]) continue; //已經解析過的跳過
allPropertyMetas[meta->_name] = meta; //將解析通過dictionary緩存
}
curClassInfo = curClassInfo.superClassInfo; //遞歸super class
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy; //復制一份解析出來的所有allPropertyMetas
// create mapper
//開始key mapper映射
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
//如果有key mapper 映射
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName]; //找到映射值的_YYModelPropertyMeta
if (!propertyMeta) return; //沒有直接返回
[allPropertyMetas removeObjectForKey:propertyName]; //有映射所以從原來的列表刪除
if ([mappedToKey isKindOfClass:[NSString class]]) { //如果是NSString映射
if (mappedToKey.length == 0) return;
propertyMeta->_mappedToKey = mappedToKey; //設置mapper key
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."]; //如果是包含"."代表key的路徑映射
for (NSString *onePath in keyPath) {
if (onePath.length == 0) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
if (keyPath.count > 1) { //>1說明有路徑映射
propertyMeta->_mappedToKeyPath = keyPath; //設置_mappedToKeyPath
[keyPathPropertyMetas addObject:propertyMeta]; //添加路徑映射對象
}
propertyMeta->_next = mapper[mappedToKey] ?: nil; //如果包含上一個同樣key值的_YYModelPropertyMeta對象慷丽,則設置next指針
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) { //如果是nsarray映射
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) { //多一級nsarray的遍歷,內容與nsstring相同
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; //多級的映射值會存到_mappedToKeyArray
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil; //同樣鳄哭,如果包含上一個同樣key值的_YYModelPropertyMeta對象要糊,則設置next指針
mapper[mappedToKey] = propertyMeta;
}
}];
}
//映射完后,遍歷所有的allPropertyMetas妆丘,設置mapper和next指針
[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; //設置classinfo
_keyMappedCount = _allPropertyMetas.count; //設置property數(shù)量
_nsType = YYClassGetNSType(cls); //獲得本類的oc foundation類型
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
/// Returns the cached model class meta
//返回緩存的model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache; //class的_YYModelMeta緩存
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]; //重新構造 _YYModelMeta緩存
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;
}
實現(xiàn)處理了很多key mapper的映射問題珊膜,但本身邏輯并不復雜。
解析邏輯
//id json對象轉化為 dictioanry的方法
+ (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;
}
//先轉化json對象到dictionary宣脉,再調用yy_modelWithDictionary
+ (instancetype)yy_modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self yy_modelWithDictionary:dic];
}
//解析model屬性并附值
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
//解析class得到modelmeta對象
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
//本地class類型映射
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
//附值函數(shù)
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
}
//附值函數(shù)
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
//本地dictionary值替換
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
//遍歷dictioanry并附值
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);
}
//本地property驗證
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
附值這塊车柠,就只取ModelSetWithDictionaryFunction
設置
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); //取_YYModelMeta
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; //取_YYModelPropertyMeta
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) { //循環(huán)遍歷propertyMeta
if (propertyMeta->_setter) {
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta); //設置值
}
propertyMeta = propertyMeta->_next;
};
}
而ModelSetValueForProperty
的設置函數(shù),就是根據(jù)不同類型塑猖,去調用msgSend調用相應的meta->_setter方法竹祷,進行附值,代碼較長羊苟,這里也不貼了塑陵。
至此,整個YYModel的解析就完成了蜡励。
總結
YYModel在整體的解析過程中令花,分三步:
- 先將Class的Method阻桅,Property,Ivar分別解析緩存兼都,構成Class的緩存
- 構建
_YYModelPropertyMeta
緩存Property結構體嫂沉,再_YYModelMeta
緩存_YYModelPropertyMeta
結構。
3.根據(jù)解析出來的_YYModelPropertyMeta
與相應的YYEncodingType等屬性扮碧,進行相應值的設置
JSONModel與YYModel對比
對比 | JSONModel | YYModel |
---|---|---|
解析方式 | 只解析property attribute encode string | 解析Class Method,Property,Ivar緩存趟章,再構造_YYModelPropertyMeta 與_YYModelMeta
|
附值方式 | KVC | 先解析出property的類型,使用msgSend調用meta->setter方法分類型附值 |
緩存方式 | AssociatedObject | CFMutableDictionaryRef |
包含類型方式 | 定義同名protocol慎王,聲明protocol | 不僅可以聲明protocol定義蚓土,還可以實現(xiàn)modelContainerPropertyGenericClass 接口 |
映射值為路徑解析 | 無 | 有 |
model類型轉換 | 無 | 有 |
懶加載 | 有 | 無 |
是否可以缺省 | 通過protocol optional | 黑名單\白名單 |