iOS開發之遍曆Model類的屬性並完善使用Runtime給Model類賦值,iosmodel
在上篇部落格《iOS開發之使用Runtime給Model類賦值》中介紹了如何使用運行時在實體類的基類中添加給實體類的屬性賦值的方法,這個方法的前提是字典的Key必須和實體類的Property Name相同,然後通過運行時來產生和執行Setter方法給Model類的屬性賦值。
通過Runtime來給Model類屬性賦值的好處是多多的,它便於代碼的後期維護,並且提高了開發效率。當你拿到解析後的字典時你不用一個一個的通過key去把字典的值賦值給相應的Model類的屬性,本篇部落格中會給出如何去遍曆Model中屬性的值,並且給出字典的Key和Model的屬性名稱不一樣的情況我們該如何負值。
接下來會在上一個部落格代碼基礎上在Model基類中添加通過Runtime來遍曆Model類的屬性值。
一、擷取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 //設定target23 [invocation setTarget:self];24 25 //設定selector26 [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的屬性值,調用代碼如下所示:
1 BeautifulGirlModel *beautifulGirl = [BeautifulGirlModel modelWithDictionary:data];2 3 [beautifulGirl displayCurrentModleProperty];
運行結果如下,下面的輸出結果是Model中屬性的值。
三、Dictionary的Key與Model的屬性不同的處理方式
有時候會遇到字典的key和Model的屬性不一樣的情況,那麼如何去解決這個問題呢?最簡單的做法是在具體的實體類中去維護一個映射關係方法,通過這個方法我們可以擷取相應的的映射關係。
1.在Model的基類中添加一個返回映射字典的一個方法,然後在子類中進行重寫,這個映射方法在基類中返回nil, 如果子類需要重寫的話就對這個方法進行重寫並返回映射字典。方法如下:
1 #pragma 返回屬性和字典key的映射關係2 -(NSDictionary *) propertyMapDic{3 return nil;4 }
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 : BaseModelObject12 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 BadBoyModel12 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並輸出結果,當然之前的代碼也是可以使用的。
1 BadBoyModel *badBoyModel = [BadBoyModel modelWithDictionary:data1];2 3 [badBoyModel displayCurrentModleProperty];
運行輸出結果如下:
今天部落格就到這,至此,Model的基類最基本的方法封裝的也就差不多了,根據具體需求可以在添加新的方法