1.YYModel
首先YYModel是以類別的方式對(duì)NSObject、NSArray逸寓、NSDictionary進(jìn)行的擴(kuò)展居兆,這樣的直接好處就是對(duì)你自己的工程代碼入侵性小,非常好接入竹伸,并且有一個(gè)YYModel的協(xié)議用來(lái)滿足你自己的定制需求泥栖。
2._YYModelMeta 和 _YYModelPropertyMeta
先看兩個(gè)很重要的類
_YYModelMeta:
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
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;
}
@end
解釋下這些屬性:
- YYClassInfo *_classInfo;
Model類的YYClassInfo簇宽,構(gòu)造_YYModelMeta的時(shí)候同時(shí)構(gòu)造生成。(關(guān)于YYClassInfo的詳細(xì)吧享,請(qǐng)看上一篇) - NSDictionary *_mapper;
核心索引魏割,要把源json轉(zhuǎn)為Model就要靠此map把屬性一一對(duì)應(yīng)。 - _mapper的key是Model的屬性名稱钢颂,如果Model有定義modelCustomPropertyMapper钞它,則有映射關(guān)系的屬性在_mapper中的key是映射的那個(gè)值。
- _mapper的value是對(duì)應(yīng)屬性的_YYModelPropertyMeta殊鞭,_YYModelPropertyMeta對(duì)該屬性有詳細(xì)的描述遭垛,以保證在json和model的屬性對(duì)應(yīng)關(guān)系成立及轉(zhuǎn)換時(shí)賦值成功等。_YYModelPropertyMeta的介紹操灿,在下面說(shuō)耻卡。
- _mapper的key是已經(jīng)通過(guò)黑白名單過(guò)濾的。
- NSArray *_allPropertyMetas;
可以理解為 _mapper.allValues - NSArray *_keyPathPropertyMetas;
在Model的modelCustomPropertyMapper映射時(shí)牲尺,可能會(huì)有路徑映射,這樣的propertyMetas會(huì)存放在這個(gè)數(shù)組中 - NSArray *_multiKeysPropertyMetas;
在Model的modelCustomPropertyMapper映射時(shí)幌蚊,可能會(huì)有一個(gè)屬性對(duì)應(yīng)json源數(shù)據(jù)的多個(gè)字段谤碳,這樣的propertyMetas會(huì)存放在這個(gè)數(shù)組中 - NSUInteger _keyMappedCount;
_keyMappedCount = _allPropertyMetas.count - YYEncodingNSType _nsType;
Model類的類型
再看下_YYModelMeta的些實(shí)現(xiàn):
-
_YYModelMeta是有緩存的,每次讀取對(duì)應(yīng)Model的_YYModelMeta時(shí)會(huì)根據(jù)是否有緩存或是否有更新來(lái)決定取緩存或生成一個(gè)新的meta
/// 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; }
創(chuàng)建流程
其實(shí)很簡(jiǎn)單溢豆,看代碼即懂:獲取到該Model的YYClassInfo
獲取到該Model的容器屬性存放的對(duì)象的類的map
遍歷YYClassInfo.propertyInfos來(lái)創(chuàng)建對(duì)應(yīng)的存放_(tái)YYModelPropertyMeta數(shù)組
創(chuàng)建對(duì)應(yīng)的mapper
給_YYModelMeta屬性賦值
_YYModelPropertyMeta:
/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
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.
}
@end
其實(shí)大部分屬性看一眼名字或作者的注釋就知道什么意思什么用了蜒简,就不做解釋了,重點(diǎn)解釋幾個(gè)一眼看不出來(lái)具體什么作用的解釋:
-
BOOL _isKVCCompatible;
標(biāo)記該屬性能否支持 key-value 模式漩仙,代碼中是這樣搓茬,一看便知: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; }
-
BOOL _isStructAvailableForKeyedArchiver;
是否支持歸檔,支持的是如下的: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; }
BOOL _hasCustomClassFromDictionary;
是否有自定義的屬性映射队他,主要看該屬性所屬的Model類中是否有modelCustomPropertyMapper_YYModelPropertyMeta *_next;
在映射的類型中卷仑,會(huì)出現(xiàn)多個(gè)屬性對(duì)應(yīng)json中同一個(gè)字段的情況,而這里的_next就會(huì)指向和該屬性同一個(gè)映射字段的下一個(gè)屬性_YYModelPropertyMeta麸折,形成一個(gè)鏈表樣式的結(jié)構(gòu)锡凝。這樣在給某個(gè)屬性賦值時(shí),就可以同時(shí)把同一個(gè)映射字段的屬性全部賦值垢啼,增加性能窜锯。
3.幾個(gè)私有方法
ModelSetValueForProperty
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta)
這個(gè)是用于給Model屬性賦值的時(shí)候,代碼太長(zhǎng)就不貼了芭析,邏輯也很簡(jiǎn)單锚扎,一句話說(shuō)一下就是:
根據(jù)meta的類型,然后用 objc_msgSend 方法去動(dòng)態(tài)調(diào)用 meta->setter 去把value賦值給屬性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;
};
}
用字典作為源來(lái)給model賦值馁启,用字典的key找到對(duì)應(yīng)的_YYModelMeta對(duì)象驾孔,用value作為賦的值,執(zhí)行ModelSetValueForProperty, 并且同時(shí)對(duì)相同映射的屬性賦值-
ModelSetWithPropertyMetaArrayFunction
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;if (propertyMeta->_mappedToKeyArray) { value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray); } else if (propertyMeta->_mappedToKeyPath) { value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath); } else { value = [dictionary objectForKey:propertyMeta->_mappedToKey]; } if (value) { __unsafe_unretained id model = (__bridge id)(context->model); ModelSetValueForProperty(model, value, propertyMeta); } }
用meta來(lái)給model賦值助币,這里比用字典來(lái)賦值多了一點(diǎn)東西就是浪听,他會(huì)根據(jù)meta的映射類型,去取出對(duì)應(yīng)的value再去賦值眉菱。
4.一般的YYModel工作流程
我們用的最多的就是 yy_modelWithJSON :
+ (instancetype)yy_modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self yy_modelWithDictionary:dic];
}
_yy_dictionaryWithJSON 主要是把傳進(jìn)來(lái)的json參數(shù)轉(zhuǎn)化為字典類型迹栓,然后供后邊使用
然后看 yy_modelWithDictionary :
+ (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;
}
這里在拿到_YYModelMeta之后,判斷了下modelMeta->_hasCustomClassFromDictionary俭缓,這個(gè)是看是否需要在json到model對(duì)象轉(zhuǎn)換中創(chuàng)建不同類的實(shí)例克伊。
然后最主要看轉(zhuǎn)換過(guò)程 yy_modelSetWithDictionary,具體看注釋:
- (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)]; //獲取到對(duì)應(yīng)的modelMeta华坦,這里有完整的映射關(guān)系愿吹,和屬性meta描述
if (modelMeta->_keyMappedCount == 0) return NO; //如果map里沒(méi)有映射關(guān)系,返回NO
//模型轉(zhuǎn)換的前處理惜姐,看使用者是否需要在模型轉(zhuǎn)換前對(duì)dic做一些啥特殊處理犁跪,返回處理后的dic
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
//對(duì)應(yīng)到結(jié)構(gòu)體里
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
//轉(zhuǎn)換流程
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
//如果model的屬性數(shù)量,大于等于json源字段的數(shù)量歹袁,說(shuō)明model中可能存在多個(gè)屬性對(duì)應(yīng)一個(gè)json源字段的或者有無(wú)法和json源字段對(duì)應(yīng)的
//讓dic里的元素全部執(zhí)行一遍ModelSetWithDictionaryFunction坷衍,即賦值流程
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
//如果model里有路徑映射的,則都執(zhí)行一遍ModelSetWithPropertyMetaArrayFunction条舔,對(duì)路徑映射的屬性賦值
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
//如果model里有多屬性映射的的枫耳,則都執(zhí)行一遍ModelSetWithPropertyMetaArrayFunction
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
//如果model的屬性數(shù)量小于json源字段的數(shù)量,那只需要找到model屬性對(duì)應(yīng)的json字段就可以孟抗,則用ModelSetWithPropertyMetaArrayFunction轉(zhuǎn)換
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
//轉(zhuǎn)換后處理迁杨,看使用者是否需要些額外處理
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
這就完成了轉(zhuǎn)換,完美~~~