Runtime之字典转模型实战

先来看看怎么使用Runtime给模型类赋值

iOS开发中的Runtime可谓是功能强大,同时Runtime使用起来也是非常灵活的,今天博客的内容主要就是使用到一丁点的Runtime的东西。好废话不多说了进入今天的整体。

一、创建我们的测试工程

在本测试工程中使用不到iOS开发的UI部分,所以我们就创建一个基于系统控制台的工程,主调用代码当然是放到main函数中了,Project创建过程如下图所示,Create new project -> OS X -> Application -> Command Line Tool ->一路next即可

二、创建我们的测试数据

1.首先使用for循环创建一个字典,当然字典的key和value在这是有规律的,下面的for循环是创建我们的测试数据,如果在有网络请求的状态下,该测试字典的来源就是你从网络请求的JOSN解析出来的字典,在这儿没有进行网络请求,因为网络请求不是本篇博客的重点,所以就使用for循环生成一个测试字典以供使用。创建测试字典的代码如下,改代码的位置放在main函数当中:

 1 NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithCapacity:11];
2
3 //创建测试适用的字典
4 for(int i = 0; i <= 10; i ++){
5 NSString *key = [NSString stringWithFormat:@"girl%d", i];
6
7 NSString *value = [NSString stringWithFormat:@"我是第%d个女孩", i];
8
9 [data setObject:value forKey:key];
10 }

上述代码生成字典,打印结果如下,可以看出字典是无序的,接下来就将data这个字典作为我们网络请求JSON解析后的字典来使用。

 1 2015-07-20 22:33:15.742 BaseModelProject[65321:3224966] data = {
2 girl0 = "我是第0个女孩";
3 girl1 = "我是第1个女孩";
4 girl10 = "我是第10个女孩";
5 girl2 = "我是第2个女孩";
6 girl3 = "我是第3个女孩";
7 girl4 = "我是第4个女孩";
8 girl5 = "我是第5个女孩";
9 girl6 = "我是第6个女孩";
10 girl7 = "我是第7个女孩";
11 girl8 = "我是第8个女孩";
12 girl9 = "我是第9个女孩";
13 }

三、创建data字典对应的实体类

接下来将会创建Data字典对应的实体类,首先将会实现实体类的属性名和字典的key值一致的情况,然后在下篇博客中将会实现如何把不同key值的字典转换成实体类的属性。

1、首先我们先创建一个实体类,这个实体类要继承与实体基类,因为一些公用的方法是在实体基类中进行编写的,如便利构造器,便利初始化方法,把字典转成Model属性等方法回被抽象到这个基类当中。创建实体类如下图所示,创建类的时候选中创建的基类即可:

2. 这个实体类的命名为:BeautifulGirlModel,下面是BeautifulGirlModel中的属性,属性的名字和字典key的值相同,如下所示,BaseModelObject是之前创建的基类,BaseModelObject继承与NSObject即可。

 1 //
2 // BeautifulGirlModel.h
3 // BaseModelProject
4 //
5 // Created by Mr.LuDashi on 15/7/20.
6 // Copyright (c) 2015年 ludashi. All rights reserved.
7 //
8
9 #import "BaseModelObject.h"
10
11 @interface BeautifulGirlModel : BaseModelObject
12
13 @property (nonatomic, copy) NSString *girl0;
14 @property (nonatomic, copy) NSString *girl1;
15 @property (nonatomic, copy) NSString *girl2;
16 @property (nonatomic, copy) NSString *girl3;
17 @property (nonatomic, copy) NSString *girl4;
18 @property (nonatomic, copy) NSString *girl5;
19 @property (nonatomic, copy) NSString *girl6;
20 @property (nonatomic, copy) NSString *girl7;
21 @property (nonatomic, copy) NSString *girl8;
22 @property (nonatomic, copy) NSString *girl9;
23 @property (nonatomic, copy) NSString *girl10;
24
25 @end

四、实现实体基类中的方法。

实体基类中的方法是从各个Model中抽象出来的并且可以重复利用的部分,在实体基类的方法中大致包括:生成getter方法,生成setter方法,获取Model类的属性,把字典的值赋给对应的Model, 动态的调用getter方法对实体类的属性值进行遍历。本篇博客中回给出一部分,剩下的一部分会在以后的博客中陆续给出。

1.根据Key值生成set方法

首先要编写的方法是传入一个字符串,然后返回该字符串所对应属性的setter方法。这个方法其实很简单的,就是把对应的字符串的首字母大写并且拼接上set关键字,再生产SEL并返回,该方法的具体实现如下:

 1 #pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回
2 - (SEL) creatSetterWithPropertyName: (NSString *) propertyName{
3
4 //1.首字母大写
5 propertyName = propertyName.capitalizedString;
6
7 //2.拼接上set关键字
8 propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
9
10 //3.返回set方法
11 return NSSelectorFromString(propertyName);
12 }

2.把字典传入到方法中,并把字典的值赋给相应实体类的属性,该方法需要调用上述方法来生成setter方法,通过setter方法把字典的Value赋值给实体类对应的属性,代码如下,下面代码中的注释还是比较详细的,具体细节就参考下面注释的内容了。通过调用这个方法就可以把字典的值赋给对应的实体类的属性,调用这个方法的前提是要求字典的key与实体类的属性名必须相同。有的小伙伴会问如果不一样该怎么做呢?这个问题到下篇博客中在进行介绍。

 1 /************************************************************************
2 *把字典赋值给当前实体类的属性
3 *参数:字典
4 *适用情况:当网络请求的数据的key与实体类的属性相同时可以通过此方法吧字典的Value
5 * 赋值给实体类的属性
6 ************************************************************************/
7
8 -(void) assginToPropertyWithDictionary: (NSDictionary *) data{
9
10 if (data == nil) {
11 return;
12 }
13
14 ///1.获取字典的key
15 NSArray *dicKey = [data allKeys];
16
17 ///2.循环遍历字典key, 并且动态生成实体类的setter方法,把字典的Value通过setter方法
18 ///赋值给实体类的属性
19 for (int i = 0; i < dicKey.count; i ++) {
20
21 ///2.1 通过getSetterSelWithAttibuteName 方法来获取实体类的set方法
22 SEL setSel = [self creatSetterWithPropertyName:dicKey[i]];
23
24 if ([self respondsToSelector:setSel]) {
25 ///2.2 获取字典中key对应的value
26 NSString *value = [NSString stringWithFormat:@"%@", data[dicKey[i]]];
27
28 ///2.3 把值通过setter方法赋值给实体类的属性
29 [self performSelectorOnMainThread:setSel
30 withObject:value
31 waitUntilDone:[NSThread isMainThread]];
32 }
33
34 }
35
36 }

3.接下来就是开始编写实体类的入口了,也就是便利初始化方法和便利构造器。并在头文件中留出接口,下面先给出便利初始化方法然后在给出便利构造器。

(1)下面的代码是实体类的便利初始化方法,当然是实例方法,该方法需要传入一个字典,这个字典中的key就是该实体类的属性名,值就是要给该实体类的属性赋的值。并且返回该实体类的实例,简单的说就是调用-(void) assginToPropertyWithDictionary: (NSDictionary *) data方法,具体代码如下:

1 - (instancetype)initWithDictionary: (NSDictionary *) data{
2 {
3 self = [super init];
4 if (self) {
5 [self assginToPropertyWithDictionary:data];
6 }
7 return self;
8 }
9 }

(2)下面将要给出便利构造器,当然便利构造器是类方法,并且返回该类的一个实例。用大白话说,便利构造器就是分配内存后调用便利初始化方法然后返回该对象的实例,下方是实体类对应的便利构造器:

+ (instancetype)modelWithDictionary: (NSDictionary *) data{

     return [[self alloc] initWithDictionary:data];

 }

五、测试上面的代码

上面运行这么多了得运行一下代码看一下结果吧。因为我们这是基于系统的控制台程序,所以我们需要在main函数中进行调用我们编写的方法,需要把我们上面生成的测试字典传入到实体类的构造器中,并且获取实体类的一个实例。该获取的实体类的实例中的属性就已经被赋值上了传入的字典的值。具体调用方法如下所示。

BeautifulGirlModel *beautifulGirl = [BeautifulGirlModel modelWithDictionary:data];

NSLog(@"%@", beautifulGirl.girl0);

运行结果如下:

最后给出最笨的赋值方法,也是最容易学会的赋值方法,不过这种方式维护起来回不太方便,而且大部分做的都是体力活的,实例如下:

1 BeautifulGirlModel *beautifulGirl1 = [[BeautifulGirlModel alloc] init];
2 beautifulGirl1.girl0 = data[@"girl0"];
3 beautifulGirl1.girl1 = data[@"girl1"];
4 beautifulGirl1.girl2 = data[@"girl2"];
5 beautifulGirl1.girl3 = data[@"girl3"];
6 beautifulGirl1.girl4 = data[@"girl4"];
7 beautifulGirl1.girl5 = data[@"girl5"];
8 beautifulGirl1.girl6 = data[@"girl6"];
9 beautifulGirl1.girl7 = data[@"girl7"];

下来会在上一个博客代码基础上在Model基类中添加通过Runtime来遍历Model类的属性值。

然后我们就讲Runtime应用到实际开发中去给模型属性赋值(当属性很多的时候非常有用)

一、获取Model的实体属性

1.要想遍历Model类的属性,首先得通过Runtime来获取该Model类有哪些属性,输出Model的所有属性的值可不像遍历Dictionary和Array那样一个for循环搞定的,下面的方法是通过Runtime来获取Model类的属性字符串,并以数组的形式返回。代码如下:

 1 ///通过运行时获取当前对象的所有属性的名称,以数组的形式返回
2 - (NSArray *) allPropertyNames{
3 ///存储所有的属性名称
4 NSMutableArray *allNames = [[NSMutableArray alloc] init];
5
6 ///存储属性的个数
7 unsigned int propertyCount = 0;
8
9 ///通过运行时获取当前类的属性
10 objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
11
12 //把属性放到数组中
13 for (int i = 0; i < propertyCount; i ++) {
14 ///取出第一个属性
15 objc_property_t property = propertys[i];
16
17 const char * propertyName = property_getName(property);
18
19 [allNames addObject:[NSString stringWithUTF8String:propertyName]];
20 }
21
22 ///释放
23 free(propertys);
24
25 return allNames;
26 }

2.获取到Model类的属性方法后需要把属性字符串生成get方法,我们可以执行get方法来获取Model属性的值,下方的方法是根据属性字符串来获取属性的getter方法,OC中属性的getter方法的名字和属性的名字是一致的,生成getter方法比较简单,具体代码如下:

1 #pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回
2 - (SEL) creatGetterWithPropertyName: (NSString *) propertyName{
3
4 //1.返回get方法: oc中的get方法就是属性的本身
5 return NSSelectorFromString(propertyName);
6 }

二、Get方法的执行

接下来要做的是通过Runtime来执行Getter方法,这一块需要通过方法的签名来执行Getter方法。在OC的运行时中要执行的方法需要传入参数或者需要接收返回值时,需要通过方法的签名来调用方法。下面的代码就是创建方法的签名,然后通过签名来获取调用的对象,在下边的方中回调用上述两个方法在通过方法的签名来获取Model属性的值,具体代码如下:

 1 - (void) displayCurrentModleProperty{
2
3 //获取实体类的属性名
4 NSArray *array = [self allPropertyNames];
5
6 //拼接参数
7 NSMutableString *resultString = [[NSMutableString alloc] init];
8
9 for (int i = 0; i < array.count; i ++) {
10
11 //获取get方法
12 SEL getSel = [self creatGetterWithPropertyName:array[i]];
13
14 if ([self respondsToSelector:getSel]) {
15
16 //获得类和方法的签名
17 NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
18
19 //从签名获得调用对象
20 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
21
22 //设置target
23 [invocation setTarget:self];
24
25 //设置selector
26 [invocation setSelector:getSel];
27
28 //接收返回的值
29 NSObject *__unsafe_unretained returnValue = nil;
30
31 //调用
32 [invocation invoke];
33
34 //接收返回值
35 [invocation getReturnValue:&returnValue];
36
37 [resultString appendFormat:@"%@\n", returnValue];
38 }
39 }
40 NSLog(@"%@", resultString);
41
42 }

执行上述方法就可以输入Model中的属性的值,下面就在main函数中对Model赋完值后调用上述方法输出一下Model的属性值,调用代码如下所示:

BeautifulGirlModel *beautifulGirl = [BeautifulGirlModel modelWithDictionary:data];

         [beautifulGirl displayCurrentModleProperty];

运行结果如下,下面的输出结果是Model中属性的值。

三、Dictionary的Key与Model的属性不同的处理方式

有时候会遇到字典的key和Model的属性不一样的情况,那么如何去解决这个问题呢?最简单的做法是在具体的实体类中去维护一个映射关系方法,通过这个方法我们可以获取相应的的映射关系。

#pragma 返回属性和字典key的映射关系
-(NSDictionary *) propertyMapDic{
return nil;
}

2.修改一下我们的便利初始化方法,在有映射字典的情况和没有映射字典的情况下调用的方法是不一样的,便利初始化方法的代码如下:

 1 - (instancetype)initWithDictionary: (NSDictionary *) data{
2 {
3 self = [super init];
4 if (self) {
5 if ([self propertyMapDic] == nil) {
6 [self assginToPropertyWithDictionary:data];
7 } else {
8 [self assginToPropertyWithNoMapDictionary:data];
9 }
10 }
11 return self;
12 }
13 }

3.接下来就将实现有映射关系要调用的方法,这个方法就是通过映射关系把字典的key转换成与property的名字一样的字典,然后调用之前的赋值方法,具体代码如下:

 1 #pragma 根据映射关系来给Model的属性赋值
2 -(void) assginToPropertyWithNoMapDictionary: (NSDictionary *) data{
3 ///获取字典和Model属性的映射关系
4 NSDictionary *propertyMapDic = [self propertyMapDic];
5
6 ///转化成key和property一样的字典,然后调用assginToPropertyWithDictionary方法
7
8 NSArray *dicKey = [data allKeys];
9
10
11 NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:dicKey.count];
12
13 for (int i = 0; i < dicKey.count; i ++) {
14 NSString *key = dicKey[i];
15 [tempDic setObject:data[key] forKey:propertyMapDic[key]];
16 }
17
18 [self assginToPropertyWithDictionary:tempDic];
19
20 }

4.创建一个BadBoyModel, 并重写propertyMapDic方法,并且在propertyMapDic方法中给出映射关系并返回该映射关系对应的字典。

(1)BadBoyModel的属性如下:

 1 //
2 // BadBoyModel.h
3 // BaseModelProject
4 //
5 // Created by Mr.LuDashi on 15/7/24.
6 // Copyright (c) 2015年 ludashi. All rights reserved.
7 //
8
9 #import "BaseModelObject.h"
10
11 @interface BadBoyModel : BaseModelObject
12
13 @property (nonatomic, copy) NSString *boy1;
14 @property (nonatomic, copy) NSString *boy2;
15 @property (nonatomic, copy) NSString *boy3;
16 @property (nonatomic, copy) NSString *boy4;
17
18 @end
1
<span style="line-height: 1.5; font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">(2)重写映射方法,映射字典的key是要转换字典的key, Value是对应Model的属性名。</span>
 1 //
2 // BadBoyModel.m
3 // BaseModelProject
4 //
5 // Created by Mr.LuDashi on 15/7/24.
6 // Copyright (c) 2015年 ludashi. All rights reserved.
7 //
8
9 #import "BadBoyModel.h"
10
11 @implementation BadBoyModel
12
13 #pragma 返回属性和字典key的映射关系
14 -(NSDictionary *) propertyMapDic{
15 return @{@"keyBoy1":@"boy1",
16 @"keyBoy2":@"boy2",
17 @"keyBoy3":@"boy3",
18 @"keyBoy4":@"boy4",};
19 }
20
21 @end

5.在main函数中进行测试

(1)生成我们的数值字典,字典的key与要赋值Model的属性不同,下面的循环就是要生成测试使用的数据:

 1 //生成Dic的Key与Model的属性不一样的字典。
2
3 NSMutableDictionary *data1 = [[NSMutableDictionary alloc] init];
4
5 //创建测试适用的字典
6 for(int i = 1; i <= 4; i ++){
7 NSString *key = [NSString stringWithFormat:@"keyBoy%d", i];
8
9 NSString *value = [NSString stringWithFormat:@"我是第%d个坏男孩", i];
10
11 [data1 setObject:value forKey:key];
12 }

(2) 实例化Model并输出结果,当然之前的代码也是可以使用的。

BadBoyModel *badBoyModel = [BadBoyModel modelWithDictionary:data1];

[badBoyModel displayCurrentModleProperty];

运行输出结果如下:

今天博客就到这,至此,Model的基类最基本的方法封装的也就差不多了,根据具体需求可以在添加新的方法

Runtime之字典转模型实战的更多相关文章

  1. iOS开发——高级技术精选OC篇&Runtime之字典转模型实战

    Runtime之字典转模型实战 如果您还不知道什么是runtime,那么请先看看这几篇文章: http://www.cnblogs.com/iCocos/p/4734687.html http://w ...

  2. ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型

    一:KVC和KVO的学习 #import "StatusItem.h" /* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字 ...

  3. ios开发- 利用运行时(runtime)字典转模型

    现在的ios开发中,我们通常会使用MVC模式.当我们拿到数据的时候,我们要把数据转成模型使用. 一般情况我们拿到的数据都是字典.这是我们就需要将字典转成模型对象了. 当对象的属性很少的时候,我们可以直 ...

  4. 福利->KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型

    我们知道,KVC+Runtime可以做非常多的事情.有了这个,我们可以实现很多的效果. 这里来个福利,利用KVC+Runtime获取类/对象的所有成员变量.属性.方法及协议: 并利用它来实现字典转模型 ...

  5. 利用runTime,实现以模型为主的字典转模型(注意与KVC的区别)

    将字典转化为模型,面向模型开发,是在开发中最为常用的功能.利用KVC可以将字典转换为模型,但是前提有三个约束,一个是必须保证模型的属性个数大于等于字典个数,二是属性名称与字典的key必须相同,三是对于 ...

  6. 利用Runtime实现简单的字典转模型

    前言 我们都知道,开发中会有这么一个过程,就是将服务器返回的数据转换成我们自己定义的模型对象.当然服务器返回的数据结构有xml类型的,也有json类型的.本文只讨论json格式的. 大家在项目中一般是 ...

  7. 使用Runtime的objc_msgSend实现模型和字典的互转

    一.介绍 模型转字典,字典转模型,这是开发中最基本的功能.系统类中提供了一个setValuesForKeysWithDictionary方法来实现字典转模型,至于模型转字典,这个就需要使用runtim ...

  8. iOS彩票项目--第七天,初次读取json数据、KVC转模型技巧、运行时字典转模型以及初步对显示网页的操作并且跟踪标签

    一.初次读取json数据 二.KVC转模型技巧,这里的技巧主要解决的是字典中的key 与 模型中有的属性对应不起来的时候 的解决办法 <方法1> <方法2>运行时字典转模型,运 ...

  9. 【iOS问题】字典转模型,属性个数不匹配问题

    一.字典转模型的键值对与模型属性不匹配问题 1. 字典的键个数 < 模型的属性个数 (key 能与模型的属性匹配) 1> .KVO 方式: - setValuesForKeysWithDi ...

随机推荐

  1. typescript入门,可以一起探讨提点意见互相学习。

    typescript是js的一个超集,TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作.TypeScript是为大 ...

  2. 北京Uber优步司机奖励政策(12月25日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  3. Java Dictionary Example

    Dictionary class is the abstract class which is parent of any class which uses the key and value pai ...

  4. P1126 机器人搬重物

    P1126 机器人搬重物 题目描述 机器人移动学会(RMI)现在正尝试用机器人搬运物品.机器人的形状是一个直径1.6米的球.在试验阶段,机器人被用于在一个储藏室中搬运货物.储藏室是一个N*M的网格,有 ...

  5. Linux tcpdump命令详解(分享文章)

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...

  6. 客户端SDK测试思路

    本文来自网易云社区 作者:万春艳 是什么 客户端SDK是为第三方开发者提供的软件开发工具包,包括SDK接口.开发文档和Demo示例等.SDK和应用之间是什么关系呢?以云信即时消息服务为例,如下图所示, ...

  7. 关联分析FPGrowth算法在JavaWeb项目中的应用

    关联分析(关联挖掘)是指在交易数据.关系数据或其他信息载体中,查找存在于项目集合或对象集合之间的频繁模式.关联.相关性或因果结构.关联分析的一个典型例子是购物篮分析.通过发现顾客放入购物篮中不同商品之 ...

  8. 第四篇 与Flask相关的插件(flask-session、wtforms)

    公司中使用SQL的种方式: 1. 写Django:ORM(关系对象映射), 2. 写Flask和其他:有两种方式: (1) 原生SQL:使用原生SQL有两种选择: A. pymysql (python ...

  9. Java开发工程师(Web方向) - 01.Java Web开发入门 - 第3章.Tomcat

    第3章--Tomcat Tomcat安装与运行 Tomcat:目前最常用的基于java的web应用服务器 本课程中所有的Java代码最终都需要部署到Tomcat中运行 Tomcat的配置文件是XML的 ...

  10. LeetCode 144 ——二叉树的前序遍历

    1. 题目 2. 解答 2.1. 递归法 定义一个存放树中数据的向量 data,从根节点开始,如果节点不为空,那么 将当前节点的数值加入到 data 中 递归得到其左子树的数据向量 temp,将 te ...