iOS 中 Model 和 JSON 互相转换
基本原理
利用 runtime 原理,获取模型中所有实例变量列表,根据实例变量以此获取模型中成员变量的名称和属性类型,区分Foundation和自定义属性,需要对NSDictionary和NSArray类型做单独处理。
下面代码是一个简单的原理实现Demo,真正的框架中需要考虑很多问题,例如可以设置只有某个数组中的属性名才允许进行字典和模型的转换,将属性名换为其他key去字典中取值,白名单,黑名单等等设置
+ (instancetype)modelWithDict:(NSDictionary *)dict { id objc = [[self alloc] init]; unsigned int count = 0; Ivar *ivarList = class_copyIvarList(self, &count); for (int i = 0; i < count; i++) { // 实例变量 Ivar ivar = ivarList[i]; // 获取成员属性名 NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; NSString *key = [ivarName substringFromIndex:1]; id value = dict[key]; if (value == nil) { continue; } // 获得成员变量的类型 NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; NSLog(@"ivar - %@, type - %@", ivarName, ivarType); ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""]; ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""]; if ([value isKindOfClass:[NSDictionary class]]) { Class modelClass = NSClassFromString(ivarType); value = [modelClass modelWithDict:value]; } else if ([value isKindOfClass:[NSArray class]]) { if ([self respondsToSelector:@selector(arrayContainModelClass)]) { NSString *type = [self arrayContainModelClass][key]; Class classModel = NSClassFromString(type); NSMutableArray *arrM = [NSMutableArray array]; for (NSDictionary *dict in value) { id model = [classModel modelWithDict:dict]; if (model) { [arrM addObject:model]; } } value = arrM; } } if (value) { [objc setValue:value forKey:key]; } } return objc; }
常用的 JSON 模型转换库
- YYModel:支持自动的 JSON/Model 转换,支持定义映射过程。API 简洁,功能也比较简单。
- JSONModel:一个 JSON 模型转换库,有着比较简洁的接口。Model 需要继承自 JSONModel。
- Mantle:Github 官方团队开发的 JSON 模型转换库,Model 需要继承自 MTLModel。
- MJExtension:国内开发者"小码哥"开发的 JSON 模型库,号称性能超过 JSONModel 和 Mantle,使用简单无侵入。
性能、易用性,无浸入性
根据 ibireme 的测试结果:
-
Mantle 在各个测试中,性能都是最差的
-
JSONModel 和 MJExtension 性能相差不多,但都比 Mantle 性能高。
-
YYModel 性能高出其他几个库一个数量级,接近手写代码的效率。
-
YYModel、MJExtension 都是采用 Category 方式来实现功能,比较灵活,无侵入
-
如果数据量在很小的时候,其实没有可比性的,就好比100ms 和 300ms 的差距,用户是感知不到的,最重要的还是框架的易用性,扩展性,容错性,和无侵入性。
-
如果对性能、网络流量等有更高的要求,就不要再用 JSON 了,建议改用 protobuf/FlatBuffers 这样的方案。JSON 转换再怎么优化,在性能和流量方面还是远差于二进制格式的。
-
进行对象类型检查,避免将错误的对象类型赋值到属性,以避免潜在的 Crash 问题。 YYModel 会尝试自动转换,转换失败时留空。对部分对象进行自动转换(比如 NSString 和 NSNumber 之间的转换)
-
具体容错处理,可能没有框架的设计者有自己不同的考虑,就是想让用户自己去做容错处理。