iOS开发之runtime数据转模型
2015-07-04 本文已影响1130人
350fee9655f0
在我们的日常开发中需要对加载的一些本地数据例如plist、json等文件中的数据进行模型转化,而苹果也为我们提供了一种非常方便的键值转换方式KVC。然而KVC在某些情况下并不能保存数据的转换成功,比如必须保证模型的属性个数大于等于字典个数,也要必须属性名称与字典的key相同等。所以这次我们假设下属性名称与字典中的key不一致的时候转换方法。
首先我们还是先要尝试下使用KVC
的方式来解决这个问题
模型如下:
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *ID;
JSON数据如下:
{
"title" : "顺平侯",
"name" : "赵云",
"id" : "sph"
},
{
"title" : "恒侯",
"html" : "张飞",
"id" : "hh"
},
{
"title" : "威侯",
"html" : "马超",
"id" : "wh"
},
{
"title" : "刚侯",
"html" : "黄忠",
"id" : "gh"
},
{
"title" : "寿亭侯",
"html" : "关羽",
"id" : "sth"
}
从上面的数据对比我们不难发现,因为在OC中的id
是关键字所有我们使用ID来替代,但是这样的话就不能直接使用KVC
,所以我们需要进行相应的处理来继续使用我们的KVC转换模型。代码如下:
首先在模型.h
文件中更新一下代码,提供一个类方法进行模型转换:
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *ID;
+(instancetype) heroDict:(NSDictionary*) dict;
在.m
文件中实现该方法
+ (instancetype)itemWithDict:(NSDictionary *)dict
{
HeroItem *hero = [[self alloc]init];
[item setValuesForKeysWithDictionary:dict];
return item;
}
程序走到这里就会去模型中遍历字典当中的所有的key。所以我们要修改的地方也就是这里去重写
KVC中的setValue forKey方法.代码如下:
- (void)setValue:(id)value forKey:(NSString *)key{
//因为已经知道要修改的key所以可以直接判定相等
if ([key isEqualToString:@"id"]) {
//进行替换
[self setValue:value forKeyPath:@"ID"];
}else{
//抛回父类处理
[super setValue:value forKey:key];
}
}
程序修改到这里,基本就可以使用KVC
的方法进行转换。但是如果我们的数据有很多不一致的情况呢?那么就让我们一起来看下今天的重头戏runtime
来的转换。
上面的例子的思路是通过遍历字典当中的key
去模型中比对,而我们这次试着遍历模型,然后去字典中比对响应的key
首先在我们的模型.m
里导入我们需要的头文件
#import <objc/runtime.h>
完成这一步在模型类中就可以使用runtime了,然后我们在.m中创建一个转换的类方法
+ (instancetype)objcWithDict:(NSDictionary *)dict updateDict:(NSDictionary *)updateDict{
}
在这个方法中我们需要做的是通过runtime
来遍历模型中的属性,进行属性的比对,如果模型中的属性在字典中不存在,那么就会去updateDict
中寻找,如果updateDict
字典中存在的话就会进行转换。objctWithDict:
方法更新如下:
+ (instancetype)objcWithDict:(NSDictionary *)dict updateDict:(NSDictionary *)updateDict{
id objc = [[self alloc] init];
// 遍历模型中属性
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0 ; i < count; i++) {
Ivar ivar = ivars[i];
// 属性名称
NSString *ivarName = @(ivar_getName(ivar));
ivarName = [ivarName substringFromIndex:1];
id value = dict[ivarName];
// 模型中属性名对应字典中的key
if (value == nil) {
if (updateDict) {
NSString *keyName = updateDict[ivarName];
value = dict[keyName];
}
}
[objc setValue:value forKeyPath:ivarName];
}
return objc;
}
到了这里转换已经完成,那么我们更新下heroDict:
方法代码:
+ (instancetype)itemWithDict:(NSDictionary *)dict{
//调用方法,updateDict中的数据即为需要替换的数据
HeroItem *item = [HeroItem objcWithDict:dict updateDict:@{@"ID":@"id"}];
return item;
}