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

Runtime之字典转模型实战

如果您还不知道什么是runtime,那么请先看看这几篇文章:

http://www.cnblogs.com/iCocos/p/4734687.html

http://www.cnblogs.com/iCocos/p/4676679.html

http://www.cnblogs.com/iCocos/p/4725527.html

关于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
(2)重写映射方法,映射字典的key是要转换字典的key, Value是对应Model的属性名。
 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的基类最基本的方法封装的也就差不多了,根据具体需求可以在添加新的方法

时间: 2024-12-06 17:00:05

iOS开发——高级技术精选OC篇&Runtime之字典转模型实战的相关文章

iOS开发——高级UI之OC篇&amp;UIdatePicker&amp;UIPickerView简单使用

UIdatePicker&UIPickerView简单使用 /***********************************************************************************/ 一:UIdatePicker:(日期控件) 1.UIDatePicker什么时候用? 当用户选择日期的时候,一般弹出一个UIDatePicker给用户选择. 2.UIDatePickerios6和ios7/8的区别 下面看看使用封装的代码怎么去实现它: 因为这个比较简

iOS开发——高级技术OC篇&amp;运行时(Runtime)机制

运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档. 下面就来看看什么是运行时,我们要怎么在iOS开发中去使用它. 官方介绍: 这里我们主要关注的是最后一种! 下面来看看Runtime的相关总结 #pragma mark 获取属性成员 /********************************************************

ios开发——常用经典算法OC篇&amp;冒泡/快速

冒泡排序与快速排序 1.序言 ios开发中涉及到算法的地方还真不多,除非你的应用程序真的非常大,或者你想你的应用程序性能非常好才会去想到关于算法方面的性能优化,而在ios开发中真的能用得到的也就是关于排序的,当然如果你是做游戏的话那么你可能会涉及到不少的算法或者优化问题,但是这不是本篇文章讨论的范围. 后面的文章中,我将会给大家详细介绍八大算法. 2.冒泡排序 2.1 引出 前面的两篇博客里讲的插入排序是基于“逐个记录插入”,选择排序是基于“选择”,那么冒泡排序其实是基于“交换”.每次从第一个记

iOS开发——高级技术&amp;摇一摇功能的实现

摇一摇功能的实现 在AppStore中多样化功能越来越多的被使用了,所以今天就开始介绍一些iOS开发的比较实用,但是我们接触的比较少的功能,我们先从摇一摇功能开始 在 UIResponder中存在这么一套方法 1 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0); 2 3 - (void)motionEnded:

iOS开发——高级技术&amp;签名机制

签名机制 最近看了objc.io上第17期中的文章 <Inside Code Signing> 对应的中文翻译版 <代码签名探析> ,受益颇深,对iOS代码签名机制有了进一步的认识.想了解详细内容建议大家还是去看原文好了. 下面是对此文章的理解再结合自己之前对该部分的认识写出的学习笔记.本文的前提是已经对非对称加密有了一定的了解. 一.数字签名(digital signature) 对指定信息使用哈希算法,得到一个固定长度的信息摘要,然后再使用 私钥 (注意必须是私钥)对该摘要加密

iOS开发——高级技术&amp;广告服务

广告服务 上 面也提到做iOS开发另一收益来源就是广告,在iOS上有很多广告服务可以集成,使用比较多的就是苹果的iAd.谷歌的Admob,下面简单演示一下如何 使用iAd来集成广告.使用iAd集成广告的过程比较简单,首先引入iAd.framework框架,然后创建ADBannerView来展示广告,通常 会设置ADBannerView的代理方法来监听广告点击并在广告加载失败时隐藏广告展示控件.下面的代码简单的演示了这个过程: 1 // 2 // ViewController.m 3 // kct

iOS开发——高级技术&amp;内购服务

内购服务 大家都知道做iOS开发本身的收入有三种来源:出售应用.内购和广告.国内用户通常很少直接 购买应用,因此对于开发者而言(特别是个人开发者),内购和广告收入就成了主要的收入来源.内购营销模式,通常软件本身是不收费的,但是要获得某些特权就 必须购买一些道具,而内购的过程是由苹果官方统一来管理的,所以和Game Center一样,在开发内购程序之前要做一些准备工作(下面的准备工作主要是针对真机的,模拟器省略Provisioning Profile配置过程): 前四步和Game Center基本

iOS开发——高级技术&amp;调用地图功能的实现

调用地图功能的实现 一:苹果自带地图 学习如逆水行舟,不进则退.古人告诉我们要不断的反思和总结,日思则日精,月思则月精,年思则年精.只有不断的尝试和总结,才能让我们的工作和生活更加 轻松愉快和美好.连着做了两个大的商城外包项目,智慧城市,搜牧通,花费了近四个月的时间,终于在反复修改后完美收工.期间的困难自不必说,以后多多总结 和沟通吧.百度地图的使用之前已经发表了一篇文章,说的很详细了,这里不再涉及,言归正传,我们说一下如何调用苹果自带的地图 第一步:导入地图文件  #import <MapKi

iOS开发——高级技术&amp;广告功能的实现

广告功能的实现 iPhone/iPad的程序,即使是Free的版本,也可以通过广告给我们带来收入.前提是你的程序足够吸引人,有足够的下载量.这里,我将介绍一下程序中集成广告的方法.主要有两种广告iAd和AdMob.(还有其他多种可被植入的广告SDK,这里就不都一一介绍了)一:iAd  从iOS 4开始,Apple增加了叫做 iAd 的架构,通过它我们可以在程序中提供Apple的广告服务.Apple会支付给开发者60%的广告收入.iAd Framework中有例程,我们可以下载学习.这里,把简单的