話不多說,下面直接開始分析設置值的核心方法,代碼如下:
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
// 模型元數(shù)據(jù)
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
// 沒有key->property直接返回
if (modelMeta->_keyMappedCount == 0) return NO;
// 在這里能修改數(shù)據(jù)源字典
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
// 結(jié)構(gòu)體
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);
}
// 返回YES表明該模型可用,返回NO忽略該模型
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
其中ModelSetContext
為作者定義的結(jié)構(gòu)體,如下:
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
- modelMeta 表示模型元數(shù)據(jù)
- model 表示模型本身
- dictionary 表示數(shù)據(jù)源字典
此處有兩個分支调鬓,當model的key->property大于等于數(shù)據(jù)源字典的key泥耀、value數(shù)量時秉溉,分別設置keyPath临谱、多個key對應同一屬性的值乒裆;另外一種情況直接設置所有的屬性元數(shù)據(jù)的值套利。
CFDictionaryApplyFunction
為Core Foundation API,會為每個key/value對調(diào)用一次指定的方法鹤耍。
CFArrayApplyFunction
會為數(shù)組內(nèi)指定范圍的每個元素調(diào)用一次指定的方法肉迫。
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);
// 多個property對應一個key,循環(huán)賦值
while (propertyMeta) {
if (propertyMeta->_setter) {
// 賦值方法
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
關(guān)于賦值方法ModelSetValueForProperty
沒有什么好講的稿黄,就是根據(jù)model屬性的類型來相應的賦值喊衫,其中會有一些字符串轉(zhuǎn)換日期,字符串轉(zhuǎn)換NSNumber等的方法調(diào)用杆怕。
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) {
// 多個key對應同一屬性賦值
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
// keyPath賦值
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);
}
}
可以看到族购,多個key對應同一屬性的優(yōu)先級最高,keyPath其次陵珍,這樣在返回自定義key->property對于同一屬性多次設置時會忽略優(yōu)先級低的賦值寝杖。