Runtime之字典轉模型實戰api
先來看看怎麼使用Runtime給模型類賦值數組
iOS開發中的Runtime可謂是功能強大,同時Runtime使用起來也是很是靈活的,今天博客的內容主要就是使用到一丁點的Runtime的東西。好廢話很少說了進入今天的總體。網絡
1、建立咱們的測試工程app
在本測試工程中使用不到iOS開發的UI部分,因此咱們就建立一個基於系統控制檯的工程,主調用代碼固然是放到main函數中了,Project建立過程以下圖所示,Create new project -> OS X -> Application -> Command Line Tool ->一路next便可函數
2、建立咱們的測試數據測試
1.首先使用for循環建立一個字典,固然字典的key和value在這是有規律的,下面的for循環是建立咱們的測試數據,若是在有網絡請求的狀態下,該測試字典的來源就是你從網絡請求的JOSN解析出來的字典,在這兒沒有進行網絡請求,由於網絡請求不是本篇博客的重點,因此就使用for循環生成一個測試字典以供使用。建立測試字典的代碼以下,改代碼的位置放在main函數當中:this
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解析後的字典來使用。atom
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字典對應的實體類spa
接下來將會建立Data字典對應的實體類,首先將會實現實體類的屬性名和字典的key值一致的狀況,而後在下篇博客中將會實現如何把不一樣key值的字典轉換成實體類的屬性。3d
一、首先咱們先建立一個實體類,這個實體類要繼承與實體基類,由於一些公用的方法是在實體基類中進行編寫的,如便利構造器,便利初始化方法,把字典轉成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
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);
運行結果以下:
最後給出最笨的賦值方法,也是最容易學會的賦值方法,不過這種方式維護起來回不太方便,並且大部分作的都是體力活的,實例以下:
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中屬性的值。
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];
運行輸出結果以下:
今天博客就到這,至此,Model的基類最基本的方法封裝的也就差很少了,根據具體需求能夠在添加新的方法