iOS开发——高级技术精选OC篇&Runtime之字典转模型实战
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的基类最基本的方法封装的也就差不多了,根据具体需求可以在添加新的方法
iOS开发——高级技术精选OC篇&Runtime之字典转模型实战的更多相关文章
- iOS开发——高级UI之OC篇&UIdatePicker&UIPickerView简单使用
UIdatePicker&UIPickerView简单使用 /***************************************************************** ...
- iOS开发——高级技术精选&底层开发之越狱开发第一篇
底层开发之越狱开发第一篇 做越狱开发也有一些时间了,有很多东西想总结一下,希望给他人一些借鉴,也是自己对过去开发经历的一些总结.个人不推荐使用盗版,这里主要以技术介绍为主. 这个系列里面主要介绍怎样进 ...
- iOS开发——高级技术精选&底层开发之越狱开发第二篇
底层开发之越狱开发第二篇 今天项目中要用到检查iPhone是否越狱的方法. Umeng统计的Mobclick.h里面已经包含了越狱检测的代码,可以直接使用 /*方法名: * isJailbroken ...
- Runtime之字典转模型实战
Runtime之字典转模型实战 先来看看怎么使用Runtime给模型类赋值 iOS开发中的Runtime可谓是功能强大,同时Runtime使用起来也是非常灵活的,今天博客的内容主要就是使用到一丁点的R ...
- iOS开发——高级技术OC篇&运行时(Runtime)机制
运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...
- iOS开发--JS调用原生OC篇
JS调用原生OC篇 方式一(反正我不用) 第一种方式是用JS发起一个假的URL请求,然后利用UIWebView的代理方法拦截这次请求,然后再做相应的处理. 我写了一个简单的HTML网页和一个btn点击 ...
- ios开发——常用经典算法OC篇&冒泡/快速
冒泡排序与快速排序 1.序言 ios开发中涉及到算法的地方还真不多,除非你的应用程序真的非常大,或者你想你的应用程序性能非常好才会去想到关于算法方面的性能优化,而在ios开发中真的能用得到的也就是关于 ...
- iOS开发——高级技术&广告服务
广告服务 上 面也提到做iOS开发另一收益来源就是广告,在iOS上有很多广告服务可以集成,使用比较多的就是苹果的iAd.谷歌的Admob,下面简单演示一下如何 使用iAd来集成广告.使用iAd集成广告 ...
- iOS开发——高级技术&内购服务
内购服务 大家都知道做iOS开发本身的收入有三种来源:出售应用.内购和广告.国内用户通常很少直接 购买应用,因此对于开发者而言(特别是个人开发者),内购和广告收入就成了主要的收入来源.内购营销模式,通 ...
随机推荐
- iis最大工作进程数
IIS 6.0允许将应用程序池配置成一个Web园(Web Garden).要理解Web园的概念,可以设想这样一种情形:假设有一个IIS 5.0服务器和三个Web网站,每一个Web网站运行着相同的应用程 ...
- openssl证书制作详细教程
自签名证书及验证 模拟证书涉及的角色 创建证书目录 mkdir ~/certs cd ~/certs 认证机构.网站.浏览器/用户 mkdir root web user 机构自签名证书生成和发布 生 ...
- Fragment要点复习
做系统时间久了,应用层的很多东西都忘了,但是基础还是得巩固,下面对fragment相关的知识点简单整理一下. 一.fragment的引入 fragment是从Android3.0(API level ...
- pushState 和 replaceState
使用此方法浏览器前进后退仍然可查看对应内容 设置值 history.pushState({}, "页面标题", "xxx.html"); history.rep ...
- react-router+webpack+gulp路由实例
背景:新项目要开始了,有一种想要在新项目中使用react的冲动,应该也是一个单页面的应用,单页应用就涉及到一个路由的问题.于是最近在网上找了蛮多关于react-router的文章,也遇到了许多的坑,经 ...
- Intellij IDEA工具Java web 环境搭建
Java web 环境搭建 环境依赖 操作系统:Windows 7 64位 开发工具:IntelliJ IDEA 13.1.4 开发工具依赖环境 JDK版本:1.7+ 开发工具依赖插件 包管理:Mav ...
- node.js初学遇到的问题
是用express安装一个网站基础架构时 express -t ejs microblog 但是出来的模板引擎是jade,通过修改js也修改模板引用npm install 等等修改了index.ejs ...
- [.net 面向对象编程基础] (16) 接口
[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...
- Windbg用法详解
工作空间 WinDBG的工作空间中保存了以下几种信息 调试会话状态: 包括断点,打开的源文件,用户定义的别名(alias)等. 调试器设置:包括符号文件路径,可执行映像文件路径,源文件路径,用I+/I ...
- http学习笔记(一)
写在前面: 第一次想写系列文章,学习了一些web知识后,发现自己还有很大的不足,但又不知道该学习些什么来完善自己的知识体系,偶然在网上看到了一篇介绍http的文章,觉得对自己有一些帮助,于是想要开始学 ...