俺们知道,在iOS开发中,字典是经常出现的一个东东,那么我们直接操作字典呢,很容易产生把眼睛找瞎也找不到的BUG。为什么这么说呢?比如:本身有这么个 tmpDict["name"] 东西,而你却写成了 tmpDict["nane"] ; 这在Xcode中是不会报错的,而你的程序可能就会莫名其妙的崩掉,此时最好的解决方案就是将字典转换成模型。
在使用模型开发时,你一个点语法,点出来的属性,点错了编译器会立刻报错,干掉BUG于摇篮之中~~~~
这里我写了一个分类,主要用于将字典转换成模型:
当然,实现还不够完美~~~哈哈
当字典是这样的时候:
@{
@"name" : @"jack",
@"age" : @20,
@"height" : @"1.55",
@"dog" : @{
@"name" : @"wangcai",
@"price" : @"100",
@"bone" : @{
@"name" : @"狗骨头",
@"weight" : @"200"
}
},
@"books" : @[
@{
@"name" : @"好书",
@"price" : @10000000.6,
@"publisher" : @"清华大学出版社",
@"author" : @"张宇清"
},
@{
@"name" : @"坏书",
@"price" : @14.6,
@"publisher" : @"北京大学出版社"
}
],
@"money" : @"100"
};
我们可以看到 , 在字典中,有 普通 字符串 、double类型、int类型、dog自定义类型、NSArray类型;
现在我要做的事就是将这些数据全部转换成模型;
NSObject+Extension.h
<span style="font-size:18px;">#import <Foundation/Foundation.h>
@interface NSObject (Extension)
/**
* 假设模型中又数组类型的成员属性,那么传入该数组类型的成员属性名称,
即可得到数组中包含的自定义对象类型
*/
- (Class)classForProperty:(NSString *)name;
/**
* 提供方法,将字典传入进去
*/
- (void)setDict:(NSDictionary *)dict;
/**
* 拿对象来调用该方法,就可将字典转换为模型
*/
+ (instancetype)objectWithDict:(NSDictionary *)dict;
@end
</span>
NSObject+Extension.m
<span style="font-size:18px;">#import "NSObject+Extension.h"
#import <objc/runtime.h>
#import "Book.h"
#import "Dog.h"
@implementation NSObject (Extension)
- (void)setDict:(NSDictionary *)dict
{
Class c = self.class;
while (c && c != [NSObject class]) {
// 用来存储成员变量的数量
unsigned int outCount = 0;
// 获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList(c, &outCount);
// 遍历所有的成员变量
for (int i = 0; i<outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 成员变量 --> 属性名
key = [key substringFromIndex:1]; // name, age, books, dog , books
// 取出字典值
id value = dict[key]; // 如果是最基本的类型就直接获得,如果是特殊类型,后面会重新赋值
// 字典中没有这个key对应的值
if (value == nil) continue;
// 获得成员变量的类型
// @"NSArray", d,i, @"NSString"
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// if ([type isEqualToString:@"@\"Dog\""]) {
// value = [Dog objectWithDict:value];
// }
NSRange range = [type rangeOfString:@"@"];
if (range.location != NSNotFound) { // 能找到@ type == @"Dog"
// 如果都是 @ 开头的数据类型
type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
if (![type hasPrefix:@"NS"]) { // 如果不是NS开头,说明是普通对象类型
// NSString -- Class 通俗的说,这不是继承,而是类包含类
Class class = NSClassFromString(type);
value = [class objectWithDict:value];
}
/**
* 判断NSArray类型
*/
if ([type isEqualToString:@"NSArray"]) { // 如果为NSArray类型
NSArray * tmpArr = (NSArray*)value; // 将value强转为NSArray,
NSMutableArray * bookArr = [NSMutableArray array]; // 用于保存对象(对象来自于数组中的字典,再一次封装到新数组中)
for (int i=0; i< tmpArr.count; i++) { // 遍历数组中字典
NSDictionary * dict = tmpArr[i];
Class clazz = [self classForProperty:key]; // 通过成员变量名获得对应的类字节码类型
if (clazz) { // 判空操作,如果是nil程序会崩溃
id obj = [clazz objectWithDict:dict]; // 获得
[bookArr addObject:obj];
}
}
value = bookArr;
}
}
// 将字典中的值设置到模型上
[self setValue:value forKeyPath:key];
}
free(ivars);
c = [c superclass]; // 如果有父类就接着调用
}
}
+ (instancetype)objectWithDict:(NSDictionary *)dict
{
NSObject *obj = [[self alloc] init];
[obj setDict:dict];
return obj;
}
@end</span>