RumeTime程序员iOS 开发

runtime实战:JSON转实体-HBEntity

2016-04-08  本文已影响430人  knighthb

我们应该经常能碰到这样的场景:在网络数据回来之后需要将网络数据转化成实体(model),通常的做法是利用kvc来为实体赋值,或者利用现在已有的MJExtention、YYModel来做。

今天以我自己写的HBEntity为例解析下如何利用runtime来为实体赋值。


HBEntity简介:

HBEntity是一个利用runtime来将NSArray或NSDictionary对象转化成自定义实体的工具,支持多实体嵌套、免除了MJExtention和YYModel中白名单和黑名单的概念,同时还支持key与属性名之间的适配。

实现思路:

HBEntity实现将NSArray或NSDictionary对象转化成自定义实体的思路比较简单。我们把问题转换一下,上学的时候我们经常会遇到解y=f(x)的问题,现在把x与我们的NSArray和NSDictionary对象对应,y与我们自定义实体对应,f函数就是我们的HBEntity,我们来捋一下我们的已知条件,我们已知x和y,要求f! WTF,怎么解 。。 从x、y去推f,这个f可能有很多种啊!!我比较喜欢倒推。既然我们是要给自定义实体赋值,那我们需要知道这个实体有多少属性,每个属性是什么类型,有没有自定义getter 和setter方法等。runtime在这个时候就派上用场了。

在拿到实体所有的属性之后剩下的就是用NSDictionary或者NSArray对象(为了方便以下都简称为“对象”,我们自定义的类称为实体)赋值了。所以在这里我也不需要去过滤对象中哪些值是我不想参与赋值的(也就是blacklist),对于我来说没有blacklist和whitelist的区分。

总结起来为:

1.解析实体的属性

2.用对象的值为实体的属性赋值。

下面我们边看代码吧边bb吧。

1.解析实体的属性——runtime

解析实体的属性时我们会用到 class_copyPropertyList方法,

OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

该方法会将cls类里所有的属性都放到一个数组里返回,outCount返回的是属性的数量,如果该cls有父类,class_copyPropertyList方法并不会返回父类的属性,当然也不会将父类属性的个数计算到outCount内。这里需要说明的是objc_property_t是一个objc_property的结构体指针,objc_property有name 和 attributes,如果我们声明了一个属性

@property (nonatomic,copy,readonly)NSString *test;

这里test就是objc_property的name,可以通过property_getName来获取,而attributes可以通过property_getAttributes来获取,attributes是我们声明的属性(nonatomic,copy,readonly等)的描述,我们先来看下一个测试Demo返回的attribues,再来苹果文档里是怎么描述这一块的。

T@“NSString”,C,N,V_entityName这一串就是attributes

从上图可以看出,每个attribute都有name和value两部分组成,我们可以通过property_copyAttributeList来获得某个属性的atrributeList,每一项是objc_property_attribure_t类型,objc_property_attribure_t类型如下:

/// Defines a property attribute

typedef struct {

const char *name;          /**< The name of the attribute */

const char *value;          /**< The value of the attribute (usually empty) */

} objc_property_attribute_t;

那具体T、C、N这些符号代表的是什么意思呢?他的值又表示的是什么呢?我们求助一下官方文档。

上面是官方文档对T、C、N等做出的解答。下面再来看看他们的值代表的什么意思。

这里是官方文档的部分截图,可以看出我们可以通过T来知道该属性的类型。

官方文档里展示的例子并没有包含所有的类型,在我的HBEntity里有补充(下载地址:https://github.com/knighthb/HBEntity),感兴趣的同学可以下载下来看看。

基本上通过以上的几个方法就能确定一个类里有多少属性(不含父类的属性)、每个属性是详细信息等。

2.利用对象为实体赋值

这里基本上就是键值对映射,如果在适配器里为key和属性名做了适配,那么在赋值时将会把key对应的value赋值给对应的属性。

对于普通的类(属性里没有自定义类、没有数组等的类)这些工作已经足够了,对于比较棘手的我们就需要进行一下处理,比如对于属性是NSArray或NSMutableArray的类赋值时我们需要知道NSArray里装的是什么,也许有同学会说,这个简单,我们声明属性的时候声明为NSArray这样的形式就知道它是NSString的啦,乍一看是的,但是你用runtime根本拿不出来,因为这种写法是编译时的,只是告诉编译器array里装的是NSString,所以在runtime根本找不到。因此这块避免不了MJExtention或YYModel里指定类名的这类操作,如果有同学有好的解决方法请不吝赐教。

另外还有一个棘手的问题就是数值类型的处理,需要装箱拆箱过程,一直没有想出出去硬编码的方法,如果有同学解决过类似的问题也请不吝赐教。

最后上点Demo:

//HBTestPerson.h

#import "HBEntity.h"

#import "HBTestEntity.h"

@interface HBTestPerson : HBEntity

@property (nonatomic , copy)NSString * entityName;

@property (nonatomic , strong) NSString *entityNum;

@property (nonatomic , strong,setter=setEntityAge2:) HBTestEntity *testEntity;

@property (nonatomic , strong) NSMutableArray* testEntities;

@property (nonatomic , assign) BOOL boolTest;

@end

//HBTestPerson.m

#import "HBTestPerson.h"

@implementation HBTestPerson

- (NSDictionary *)hb_transferDic {

return @{@"entityname":@"entityName",

@"entitynum":@"entityNum"};

}

+ (NSDictionary *)hb_objectClassForKeyDic {

return @{@"testEntities":[HBTestEntity class]};

}

- (void)setEntityAge2:(HBTestEntity *)entityAge {

_testEntity = entityAge;

}

@end

//HBTestEntity.h

#import "HBEntity.h"

@interface HBTestEntity : HBEntity

@property (nonatomic , copy) NSString * name;

@property (nonatomic , copy) NSNumber * age;

@end

//HBTestEntity.m

#import "HBTestEntity.h"

@implementation HBTestEntity

- (void)setName:(NSString *)name {

_name = name;

}

@end

//HBArrayTestEntity.h

#import "HBEntity.h"

@interface HBArrayTestEntity : HBEntity

@property (nonatomic , strong) NSMutableArray * array;

@end

//HBArrayTestEntity.m

#import "HBArrayTestEntity.h"

#import "HBTestEntity.h"

@implementation HBArrayTestEntity

+ (NSDictionary *)hb_objectClassForKeyDic {

return @{@"array":[HBTestEntity class]};

}

@end

上面是几个测试类,下面是测试代码

NSDictionary * entityDic = @{@"entityname":@"hehe",

@"entitynum":@"1",

@"testEntity":@{@"name":@"xiaoming",

@"age":@(34)

},

@"testEntities":@[@{@"name":@"xiaoming",

@"age":@(34)

},

@{@"name":@"xiaoming",

@"age":@(34)

},

@{@"name":@"xiaoming",

@"age":@(34)

}]};

HBTestPerson * entity = [HBTestPerson transferEntityWithDic:entityDic];

NSArray * entityArray = @[@{@"name":@"xiaoming",

@"age":@(34)

},

@{@"name":@"xiaoming",

@"age":@(34)

},

@{@"name":@"xiaoming",

@"age":@(34)

}];

HBArrayTestEntity * arrayTestEntity = [HBArrayTestEntity transferEntityWithObject:entityArray];

结果如下:


总结:

不要脸的取了个总结,主要是为了贴下github的地址

github地址:https://github.com/knighthb/HBEntity

或者 git clone https://github.com/knighthb/HBEntity.git

也可以通过Pod 来安装 HBEntity玩耍

pod 'HBEntity'

希望通过解析HBEntity能让大家对runtime能有不一样的认识,如果大家在玩耍的过程中遇到了问题或者发现了bug请及时联系我

QQ:513179531

微信:knight_hb

或者在底下留言 ^ ^

上一篇下一篇

猜你喜欢

热点阅读