前文介绍了 YYModel 怎么字典转模型,
概述
本文继续探讨,modelMeta,存放模型的属性、和字典的键对应关系的描述文件
结构:
主要使用四个模型:
@interface YYClassPropertyInfo : NSObject
对应模型的属性
@interface YYClassInfo : NSObject
对应模型的类
包含属性字典,NSDictionary<NSString *, YYClassIvarInfo *>
@interface _YYModelPropertyMeta : NSObject
对应模型的属性,及其处理
包含一个成员变量 YYClassPropertyInfo *_info,
把模型取得的属性 YYClassPropertyInfo 及其处理,封装在一起
@interface _YYModelMeta : NSObject
对应模型的类,及其处理
包含一个成员变量 YYClassInfo *_classInfo,
把模型取得的类信息 YYClassInfo 及其处理,封装在一起
模型,为什么这么设计
下面,通过两个例子来介绍
例子一, + (NSDictionary *)modelCustomPropertyMapper
模型如下:
@interface Author : NSObject
@property NSString *name;
@property NSString *birthday;
@end
@implementation Author
@end
@interface Book : NSObject
@property NSString *name;
@property NSUInteger pages;
@property Author *reporter;
@end
@implementation Book
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"pages" : @"p",
@"reporter" : @[@"author",@"writer"]};
}
@end
代码实现
_YYModelMeta : NSObject 的实现中
先标记
- 对于
[ @"pages" : @"p" ],
通过下面的成员变量,维护一层映射关系
NSDictionary *_mapper;
- 对于
[ @"reporter" : @[@"author",@"writer"],
通过下面的成员变量,持有记录
NSArray *_multiKeysPropertyMetas;
具体实现
- (instancetype)initWithClass:(Class)cls {
// ...
NSMutableDictionary *mapper = [NSMutableDictionary new];
// ...
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
// allPropertyMetas 删除 @"pages" 对应的
// allPropertyMetas 删除 @"reporter" 对应的
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
// 处理这个 ,
// [ @"pages" : @"p" ]
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;
// 修改
// 到了这里,allPropertyMetas 中 @"pages" 对应的 propertyMeta
// 变成了 allPropertyMetas 中 @"p" 对应的 propertyMeta
mapper[mappedToKey] = propertyMeta;
// if ([mappedToKey isKindOfClass:[NSString class]])
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
// 处理这个 ,
// [@"reporter" : @[@"author",@"writer"]]
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;
// 记录属性信息
// _mappedToKeyArray 变成了, @[@"author",@"writer"]
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
// 持有属性
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
// if ([mappedToKey isKindOfClass:[NSArray class]])
}
}]; // enumerateKeysAndObjectsUsingBlock
} // if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)])
// 异常情况: [ @"pages" : @"p" ]
// 上面已经把异常情况属性的 _mappedToKey 记录了
// 下面把正常情况属性的 _mappedToKey 记录一遍
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
// ...
if (mapper.count) _mapper = mapper;
// 处理这个 ,
// [@"reporter" : @[@"author",@"writer"]]
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
// ...
}
再处理
字典转模型中,
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
// ...
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
// [ @"pages" : @"p" ] ,走下面这句
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
// ...
if (modelMeta->_multiKeysPropertyMetas) {
// [@"reporter" : @[@"author",@"writer"]],走下面这句
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
}
// ...
}
- 对于
[ @"pages" : @"p" ]
// _key 是 “p”
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
// propertyMeta->_name 是 “pages”
// 通过维护的一层映射关系 _mapper,拿到准确的 propertyMeta
__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;
};
}
- 对于
[ @"reporter" : @[@"author",@"writer"],
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);
}
// ...
// 赋值
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
多键取值, 拿到值,就完结
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
id value = nil;
for (NSString *key in multiKeys) {
if ([key isKindOfClass:[NSString class]]) {
value = dic[key];
// 获取到了,就 OK
if (value) break;
}
// ...
}
return value;
}
例子 2,+ (NSDictionary *)modelContainerPropertyGenericClass
模型如下:
@interface Character : NSObject
@property NSString *name;
@property NSString *birthday;
@end
@implementation Character
@end
@interface BookTwo : NSObject
@property NSString *name;
@property NSUInteger pages;
@property NSArray *character;
@end
@implementation BookTwo
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"character" : [Character class] };
}
@end
代码实现
先标记
- 对于
@{@"character" : [Character class] }
这次用到了 _YYModelPropertyMeta : NSObject
他下面的成员变量,记录了容器类信息
Class _genericCls;
- 具体实现
同上面一样,先回到 _YYModelMeta 的初始化方法,
// 拿到每一个属性的容器类信息
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
if (genericMapper) {
// 获取到了,自定义的容器类信息
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
// 通过这个方法 object_getClass,
// 区分 obj 是类,
// 还是对象
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
}
// ...
}];
genericMapper = tmp;
}
}
// 初始化,所有的属性信息 _YYModelPropertyMeta
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) {
// 通过自定义的属性名,过滤掉 NSObject 的自带信息
if (!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;
}
进入 _YYModelPropertyMeta 的初始化方法中,
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// ...
_YYModelPropertyMeta *meta = [self new];
// ...
// 成员变量,记录了容器类信息
meta->_genericCls = generic;
// ...
}
再处理
字典转模型中,
再次进入熟悉的配方,
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);
// ...
// 赋值方法
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
// ...
}
赋值方法中,处理容器类的数据解析
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
// ...
switch (meta->_nsType) {
// ...
case YYEncodingTypeNSArray:
case YYEncodingTypeNSMutableArray: {
if (meta->_genericCls) {
// 进入容器类
NSArray *valueArr = nil;
if ([value isKindOfClass:[NSArray class]]) valueArr = value;
else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;
if (valueArr) {
NSMutableArray *objectArr = [NSMutableArray new];
// 处理数组中,每一个容器类的实例
for (id one in valueArr) {
if ([one isKindOfClass:meta->_genericCls]) {
// 处理完成,直接添加
[objectArr addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {、
// 没处理的,处理过后,再添加
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
// 进入熟悉的解析
// 可参考前文
[newOne yy_modelSetWithDictionary:one];
if (newOne) [objectArr addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
}
// 存在容器类,不走下面的
} else {
// 本例中,不走
if ([value isKindOfClass:[NSArray class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSArray *)value).mutableCopy);
}
}
// ...
}
} break;
// ...
}
}