Runtime之字典转模型实战

Runtime之字典转模型实战javascript

 

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

 

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

1、建立咱们的测试工程数组

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

blob.png

 

2、建立咱们的测试数据app

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 }
复制代码

3、建立data字典对应的实体类this

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

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

blob.png

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
复制代码

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

实体基类中的方法是从各个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]; }

5、测试上面的代码

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

BeautifulGirlModel *beautifulGirl = [BeautifulGirlModel modelWithDictionary:data];
 
NSLog(@"%@", beautifulGirl.girl0);

运行结果以下:

blob.png

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

复制代码
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应用到实际开发中去给模型属性赋值(当属性不少的时候很是有用)

 

1、获取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 }

 

2、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中属性的值。

blob.png

3、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];

 

运行输出结果以下:

blob.png

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

相关文章
相关标签/搜索