接着講runtime的應用,也是最重要的數組
說的字典轉模型不少人會想到KVC,而後重寫測試
- (void)setValue:(id)value forUndefinedKey:(NSString *)key方法,爲何要重寫呢,由於kvc字典轉模型必須保證,模型中的屬性和字典中的key一一對應,若是不對應,系統就會調用spa
- (void)setValue:(id)value forUndefinedKey:(NSString *)key報錯指針
簡單回顧一下之前的作法啊,如今說說用runtime字典轉模型code
思路:利用運行時,遍歷模型中全部的屬性,根據模型的屬性名,去字典中查找key,取出對應的值,給模型的屬性賦值對象
步驟:提供一個NSObject分類,專門字典轉模型,之後全部模型均可以經過這個分類轉字符串
第三方JSONModel,mantle都是這麼幹的get
@implementation NSObject (Model) + (instancetype)modelWithDict:(NSDictionary *)dict { // 思路:遍歷模型中全部屬性-》使用運行時 // 0.建立對應的對象 id objc = [[self alloc] init]; // 1.利用runtime給對象中的成員屬性賦值 // class_copyIvarList:獲取類中的全部成員屬性 // Ivar:成員屬性的意思 // 第一個參數:表示獲取哪一個類中的成員屬性 // 第二個參數:表示這個類有多少成員屬性,傳入一個Int變量地址,會自動給這個變量賦值 // 返回值Ivar *:指的是一個ivar數組,會把全部成員屬性放在一個數組中,經過返回的數組就能所有獲取到。 /* 相似下面這種寫法 Ivar ivar; Ivar ivar1; Ivar ivar2; // 定義一個ivar的數組a Ivar a[] = {ivar,ivar1,ivar2}; // 用一個Ivar *指針指向數組第一個元素 Ivar *ivarList = a; // 根據指針訪問數組第一個元素 ivarList[0]; */ unsigned int count; // 獲取類中的全部成員屬性 Ivar *ivarList = class_copyIvarList(self, &count); for (int i = 0; i < count; i++) { // 根據角標,從數組取出對應的成員屬性 Ivar ivar = ivarList[i]; // 獲取成員屬性名 NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 處理成員屬性名->字典中的key // 從第一個角標開始截取 NSString *key = [name substringFromIndex:1]; // 根據成員屬性名去字典中查找對應的value id value = dict[key]; // 二級轉換:若是字典中還有字典,也須要把對應的字典轉換成模型 // 判斷下value是不是字典 if ([value isKindOfClass:[NSDictionary class]]) { // 字典轉模型 // 獲取模型的類對象,調用modelWithDict // 模型的類名已知,就是成員屬性的類型 // 獲取成員屬性類型 NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; // 生成的是這種@"@\"User\"" 類型 -》 @"User" 在OC字符串中 \" -> ",\是轉義的意思,不佔用字符 // 裁剪類型字符串 NSRange range = [type rangeOfString:@"\""]; type = [type substringFromIndex:range.location + range.length]; range = [type rangeOfString:@"\""]; // 裁剪到哪一個角標,不包括當前角標 type = [type substringToIndex:range.location]; // 根據字符串類名生成類對象 Class modelClass = NSClassFromString(type); if (modelClass) { // 有對應的模型才須要轉 // 把字典轉模型 value = [modelClass modelWithDict:value]; } } // 三級轉換:NSArray中也是字典,把數組中的字典轉換成模型. // 判斷值是不是數組 if ([value isKindOfClass:[NSArray class]]) { // 判斷對應類有沒有實現字典數組轉模型數組的協議 if ([self respondsToSelector:@selector(arrayContainModelClass)]) { // 轉換成id類型,就能調用任何對象的方法 id idSelf = self; // 獲取數組中字典對應的模型 NSString *type = [idSelf arrayContainModelClass][key]; // 生成模型 Class classModel = NSClassFromString(type); NSMutableArray *arrM = [NSMutableArray array]; // 遍歷字典數組,生成模型數組 for (NSDictionary *dict in value) { // 字典轉模型 id model = [classModel modelWithDict:dict]; [arrM addObject:model]; } // 把模型數組賦值給value value = arrM; } } if (value) { // 有值,才須要給模型的屬性賦值 // 利用KVC給模型中的屬性賦值 [objc setValue:value forKey:key]; } } return objc; } @end
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 解析Plist文件 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath]; // 獲取字典數組 NSArray *dictArr = statusDict[@"statuses"]; // 自動生成模型的屬性字符串 // [NSObject resolveDict:dictArr[0][@"user"]]; _statuses = [NSMutableArray array]; // 遍歷字典數組 for (NSDictionary *dict in dictArr) { Status *status = [Status modelWithDict:dict]; [_statuses addObject:status]; } // 測試數據 NSLog(@"%@ %@",_statuses,[_statuses[0] user]); }
給person類添加一個屬性namestring
- (NSDictionary *)getAllPropertyAndValues { NSMutableDictionary *props = [NSMutableDictionary dictionary]; unsigned int count; //獲取屬性列表 objc_property_t *properties = class_copyPropertyList([Person class], &count); for (int i = 0; i < count; i++) { objc_property_t property = properties[i]; //獲取屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; //獲取屬性值 id propertyValue = [self valueForKey:propertyName]; if (propertyValue) { [props setObject:propertyValue forKey:propertyName]; } } //釋放 free(properties); return props; }
調用看看結果it
Person *person = [[Person alloc] init]; person.name = @"jia"; NSLog(@"%@", [person getAllPropertyAndValues]);