利用反射(Reflection)来高效初始化Model的值

Json 转 Model 后,接下来要做的事情

Json转model已是很常见的事情,优秀的第三方库也很多,如YYModel
本文以YYModel为例,在它的基础上,扩展成一个在项目中使用的Model基类。Model基类内置了开关,可对一些常见的对象类型的实例初始化为“空值”(例如,NSString类型赋值为@””,NSArray类型赋值为@[]),这样的做法有2优点:
1、方便,省去了在项目中判断是否为nil的情况
2、安全,避免了一些不必要的异常

核心代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-(NSString *)getPropertyType:(objc_property_t)property {
NSString *propertyType = @"";
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
if (attrs[i].name[0] == 'T') {
if (attrs[i].value) {
NSString *typeEncoding = [NSString stringWithUTF8String:attrs[i].value];

if (typeEncoding.length > 0) {
NSScanner *scanner = [NSScanner scannerWithString:typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;

NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
if (clsName.length) propertyType = clsName;
break;
}
}
}
}
}
return propertyType;
}

objc_property_t 类型格式化整理为NSString类型,以方便后续的操作。代码里面的处理做法,在网上参考比较了几种做法,最后发现,还是YYmodel作者的做法最为细腻。取其精华,加工后完成。

核心代码2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-(void)setSafeValue{
NCBaseModelSafeType safePropertyTypeAll = [self safePropertyType];
if (safePropertyTypeAll == NCBaseModelSafeTypeNone) {
return;
}
NSMutableDictionary *safeTypeDic = [NSMutableDictionary dictionary];
if (safePropertyTypeAll & NCBaseModelSafeTypeNSString) {
[safeTypeDic setObject:@"" forKey:NSStringFromClass([NSString class])];
}
if (safePropertyTypeAll & NCBaseModelSafeTypeNSNumber) {
[safeTypeDic setObject:@(0) forKey:NSStringFromClass([NSNumber class])];
}
if (safePropertyTypeAll & NCBaseModelSafeTypeNSArray) {
[safeTypeDic setObject:@[] forKey:NSStringFromClass([NSArray class])];
}

unsigned int outCount, i;
// 包含所有Property的数组
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
// 遍历每个Property
for (i = 0; i < outCount; i++) {
// 取出对应Property
objc_property_t property = properties[i];

// 获取Property对应的变量名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
// 获取Property的类型名
NSString *propertyTypeName = [self getPropertyType:property];
// 获取Property的值
id propertyValue = [self valueForKey:propertyName];
// 值为空,才设置默认值
if (!propertyValue) {
NSString *safeTypeKey =propertyTypeName;
id safeTypeValue = safeTypeDic[safeTypeKey];
if (safeTypeValue) {
[self setValue:safeTypeValue forKey:propertyName];
}
}
}
free(properties);
}

代码里面预告设置NSString,NSNumber,NSArray这三种类型的默认值分别为:
@””,@(0),@[]。
思路上采用预先设置默认值到NSDictionary里,这样查找赋值的时间复杂度就为o(1)。函数方法里面有一个在属性数组里操作的情况,因此,总体的时间复杂度为o(n)。

在接口方面比较灵活,默认是不做转换的,避免了一些隐形操作导致的不安全感。需要打开时,在子类中重载方法即可:

1
2
3
-(NCBaseModelSafeType)safePropertyType{
return NCBaseModelSafeTypeNSString | NCBaseModelSafeTypeNSNumber | NCBaseModelSafeTypeNSArray;
}

源码链接NCBaseModel