我們在iOS開發(fā)中,一般會使用MVC或者MVVM等模式。當(dāng)我們從接口中拿到數(shù)據(jù)時,我們需要把數(shù)據(jù)轉(zhuǎn)成模型使用。下面我就帶大家一起用runtime一步一步的來完成這個轉(zhuǎn)換框架.(比較簡單的model不用runtime也可以的) .
@interface TestModel : NSObject@PRoperty (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *phone;@property (nonatomic, copy) NSString *address;@property (nonatomic, assign) NSInteger age;@end控制器ViewController中@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; self.title = @"字典轉(zhuǎn)模型"; NSDictionary *dicTest = @{@"name":@"張三", @"phone":@"110", @"age":@"10"}; TestModel *model = [TestModel yj_initWithDictionary:dicTest]; NSLog(@"model-----name:%@, phone:%@, address:%@, age:%@", model.name, model.phone, model.address, @(model.age));}@end字典轉(zhuǎn)模型的分類中@implementation NSObject (YJModelDicTransform)//字典轉(zhuǎn)模型+ (instancetype)yj_initWithDictionary:(NSDictionary *)dic{ id myObj = [[self alloc] init]; unsigned int outCount; //獲取類中的所有成員屬性 objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { objc_property_t property = arrPropertys[i]; //獲取屬性名字符串 //model中的屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; id propertyValue = dic[propertyName]; if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } free(arrPropertys); return myObj;}控制臺打印2016-12-19 15:55:18.231 YJModelDicTransform[1627:125724] model-----name:張三, phone:110, address:(null), age:10
這個地方我覺得這里是不需要用runtime啊, 比如這樣寫,同樣可以完成需求,當(dāng)然需要保證dic中key和 屬性一一對應(yīng). NSArray *keyArray = [dict allKeys]; for (NSString *key in keyArray) { [self setValue:dict[key] forKey:key] ; }這個方法也要有,防止服務(wù)器出錯時的崩潰- (void)setValue:(id)value forUndefinedKey:(NSString *)key { }到這步,我的需求基本就完了,但是還是向下看下,學(xué)習(xí)學(xué)習(xí) .2、模型中嵌套有模型
第一步完成后我們已經(jīng)可以自動完成字典和模型的轉(zhuǎn)換了,但是還不完善,比如我們的字典的value中如果有字典或者數(shù)組類型的話,程序就識別不了,所以我們現(xiàn)在就來處理這種情況
先通過runtime來獲取模型中屬性的類型,然后根據(jù)不同類型來處理
@interface TestModel : NSObject@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *phone;@property (nonatomic, copy) NSString *address;@property (nonatomic, assign) NSInteger age;//模型中嵌套UserModel模型@property (nonatomic, strong) UserModel *user;@end@interface UserModel : NSObject@property (nonatomic, copy) NSString *userName;@property (nonatomic, copy) NSString *userId;@endNSString *const YJClassType_object = @"對象類型";NSString *const YJClassType_basic = @"基礎(chǔ)數(shù)據(jù)類型";NSString *const YJClassType_other = @"其它";@implementation NSObject (YJModelDicTransform)+ (instancetype)yj_initWithDictionary:(NSDictionary *)dic{ id myObj = [[self alloc] init]; unsigned int outCount; //獲取類中的所有成員屬性 objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { objc_property_t property = arrPropertys[i]; //獲取屬性名字符串 //model中的屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; id propertyValue = dic[propertyName]; if (propertyValue == nil) { continue; } //獲取屬性是什么類型的 NSDictionary *dicPropertyType = [self propertyTypeFromProperty:property]; NSString *propertyClassType = [dicPropertyType objectForKey:@"classType"]; NSString *propertyType = [dicPropertyType objectForKey:@"type"]; if ([propertyType isEqualToString:YJClassType_object]) { if ([propertyClassType isEqualToString:@"NSArray"] || [propertyClassType isEqualToString:@"NSMutableArray"]) { //數(shù)組類型,現(xiàn)在還用不了,因為還沒有方法知道數(shù)組中保存的是什么類型,后面會處理 } else if ([propertyClassType isEqualToString:@"NSDictionary"] || [propertyClassType isEqualToString:@"NSMutableDictionary"]) { //字典類型 不考慮,一般不會用字典,用自定義model } else if ([propertyClassType isEqualToString:@"NSString"]) { //字符串類型 if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //自定義類型,循環(huán)調(diào)用,一直到不是自定義類型 propertyValue = [NSClassFromString(propertyClassType) yj_initWithDictionary:propertyValue]; if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } } else if ([propertyType isEqualToString:YJClassType_basic]) { //基本數(shù)據(jù)類型 if ([propertyClassType isEqualToString:@"c"]) { //bool類型 NSString *lowerValue = [propertyValue lowercaseString]; if ([lowerValue isEqualToString:@"yes"] || [lowerValue isEqualToString:@"true"]) { propertyValue = @(YES); } else if ([lowerValue isEqualToString:@"no"] || [lowerValue isEqualToString:@"false"]) { propertyValue = @(NO); } } else { propertyValue = [[[NSNumberFormatter alloc] init] numberFromString:propertyValue]; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //其他類型 } } free(arrPropertys); return myObj;}//獲取屬性的類型- (NSDictionary *)propertyTypeFromProperty:(objc_property_t)property{ //獲取屬性的類型, 類似 T@"NSString",C,N,V_name T@"UserModel",&,N,V_user NSString *propertyAttrs = @(property_getAttributes(property)); NSMutableDictionary *dicPropertyType = [NSMutableDictionary dictionary]; //截取類型 NSRange commaRange = [propertyAttrs rangeOfString:@","]; NSString *propertyType = [propertyAttrs substringWithRange:NSMakeRange(1, commaRange.location - 1)]; NSLog(@"屬性類型:%@, %@", propertyAttrs, propertyType); if ([propertyType hasprefix:@"@"] && propertyType.length > 2) { //對象類型 NSString *propertyClassType = [propertyType substringWithRange:NSMakeRange(2, propertyType.length - 3)]; [dicPropertyType setObject:propertyClassType forKey:@"classType"]; [dicPropertyType setObject:YJClassType_object forKey:@"type"]; } else if ([propertyType isEqualToString:@"q"]) { //NSInteger類型 [dicPropertyType setObject:@"NSInteger" forKey:@"classType"]; [dicPropertyType setObject:YJClassType_basic forKey:@"type"]; } else if ([propertyType isEqualToString:@"d"]) { //CGFloat類型 [dicPropertyType setObject:@"CGFloat" forKey:@"classType"]; [dicPropertyType setObject:YJClassType_basic forKey:@"type"]; } else if ([propertyType isEqualToString:@"c"]) { //BOOL類型 [dicPropertyType setObject:@"BOOL" forKey:@"classType"]; [dicPropertyType setObject:YJClassType_basic forKey:@"type"]; } else { [dicPropertyType setObject:YJClassType_other forKey:@"type"]; } return dicPropertyType;}控制器中- (void)viewDidLoad { [super viewDidLoad]; self.title = @"字典轉(zhuǎn)模型"; NSDictionary *dicTest = @{@"name":@"張三", @"phone":@"110", @"age":@"10", @"user":@{@"userId":@"2"}}; TestModel *model = [TestModel yj_initWithDictionary:dicTest]; NSLog(@"model-----name:%@, phone:%@, address:%@, age:%@, userId:%@, userName:%@", model.name, model.phone, model.address, @(model.age), model.user.userId, model.user.userName);}控制臺打印2016-12-19 16:39:52.079 YJModelDicTransform[1851:143085] 屬性類型:T@"NSString",C,N,V_name, @"NSString"
2016-12-19 16:39:52.080 YJModelDicTransform[1851:143085] 屬性類型:T@"NSString",C,N,V_phone, @"NSString"
2016-12-19 16:39:52.080 YJModelDicTransform[1851:143085] 屬性類型:Tq,N,V_age, q
2016-12-19 16:39:52.081 YJModelDicTransform[1851:143085] 屬性類型:T@"UserModel",&,N,V_user, @"UserModel"
2016-12-19 16:39:52.081 YJModelDicTransform[1851:143085] 屬性類型:T@"NSString",C,N,V_userId, @"NSString"
2016-12-19 16:39:52.081 YJModelDicTransform[1851:143085] model-----name:張三, phone:110, address:(null), age:10, userId:2, userName:(null)
在類中的聲明為
T@“NSNumber” 標(biāo)記了屬于什么類型N 線程安全 相當(dāng)與Objective-C中的nonmaticR 不可變,R相當(dāng)與Objective-C中的readonly,C相當(dāng)于copy V_name 去掉V_,name就是變量名通過對type進行處理就可以獲得屬性的類型。從而進行下一步處理。
注意點:class_copyPropertyList返回的僅僅是對象類的屬性,class_copyIvarList返回類的所有屬性和變量,在swift中如let a: Int? 是無法通過class_copyPropertyList返回的。
看完了,開個簡化版吧, 我是讓model都繼承rootModel, 他采用了給NSObject加類別, 都可以, 加類別 好處是 現(xiàn)有的工程幾乎不用動,壞處是,以后再擴展不太方便 , 繼承rootModel好處是 擴展容易 , 比較適合一個新的工程 .+ (id)objectWithDictionary:(NSDictionary * )dic { id myObj = [[self alloc] init]; unsigned int outCount; //獲取類中的所有成員屬性 objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { objc_property_t property = arrPropertys[i]; //獲取屬性名字符串 //model中的屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; id propertyValue = dic[propertyName]; if ([propertyValue isKindOfClass:[NSDictionary class] ]) { NSString * valueType =[self getPropertyType: property ]; Class valueClass = NSClassFromString(valueType) ; // 遞歸調(diào)用,多少層的model都可以, id value = [valueClass objectWithDictionary:propertyValue]; [myObj setValue:value forKey:propertyName]; continue ; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } free(arrPropertys); // -------------------方法二 : 不用runtime試試, 經(jīng)過拼命掙扎, 結(jié)果表明,只能對簡單模型有效,對模型嵌套無效,主要失敗原因,不能動態(tài)的獲得嵌套模型的類名,獲得類名或許只能通過runtime了// for (NSString * key in dic.allKeys) {// // [myObj setValue:dic[key] forKey:key ];// // }// return myObj;}+ (NSString *) getPropertyType:(objc_property_t)property { //獲取屬性的類型, 類似 T@"NSString",C,N,V_name T@"UserModel",&,N,V_user NSString *propertyAttrs = @(property_getAttributes(property)); //截取類型 NSRange commaRange = [propertyAttrs rangeOfString:@","]; NSString *propertyType = [propertyAttrs substringWithRange:NSMakeRange(3, commaRange.location - 4)]; NSLog(@"屬性類型:%@, 截取后%@", propertyAttrs, propertyType); return propertyType ; }3、處理模型中有數(shù)組屬性的情況
第二步之后程序可以處理模型中包含模型的情況, 但是還不能處理模型中有數(shù)組的情況,因為數(shù)組中存儲的類型需要人為的告訴程序,下面我們添加一個協(xié)議來來處理這種情況
先創(chuàng)建一個協(xié)議, 然后讓分類遵循它
@protocol YJModelDicTransform <NSObject>@optional/** * 數(shù)組中存儲的類型 * * @return key --- 屬性名, value --- 數(shù)組中存儲的類型 */+ (NSDictionary *)yj_objectClassInArray;@end@interface NSObject (YJModelDicTransform)<YJModelDicTransform>+ (instancetype)yj_initWithDictionary:(NSDictionary *)dic;@end在model中實現(xiàn)這個方法@implementation TestModel+ (NSDictionary *)yj_objectClassInArray{ return @{@"arrUsers":@"UserModel"};}+ (NSDictionary *)yj_propertykeyReplacedWithValue{ return @{@"_id":@"id"};}@end//字典轉(zhuǎn)模型+ (instancetype)yj_initWithDictionary:(NSDictionary *)dic{ id myObj = [[self alloc] init]; unsigned int outCount; //獲取類中的所有成員屬性 objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { objc_property_t property = arrPropertys[i]; //獲取屬性名字符串 //model中的屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; id propertyValue = dic[propertyName]; if (propertyValue == nil) { continue; } //獲取屬性是什么類型的 NSDictionary *dicPropertyType = [self propertyTypeFromProperty:property]; NSString *propertyClassType = [dicPropertyType objectForKey:@"classType"]; NSString *propertyType = [dicPropertyType objectForKey:@"type"]; if ([propertyType isEqualToString:YJClassType_object]) { if ([propertyClassType isEqualToString:@"NSArray"] || [propertyClassType isEqualToString:@"NSMutableArray"]) { //數(shù)組類型 if ([self respondsToSelector:@selector(yj_objectClassInArray)]) { id propertyValueType = [[self yj_objectClassInArray] objectForKey:propertyName]; if ([propertyValueType isKindOfClass:[NSString class]]) { propertyValue = [NSClassFromString(propertyValueType) yj_initWithArray:propertyValue]; } else { propertyValue = [propertyValueType yj_initWithArray:propertyValue]; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } } else if ([propertyClassType isEqualToString:@"NSDictionary"] || [propertyClassType isEqualToString:@"NSMutableDictionary"]) { //字典類型 不考慮,一般不會用字典,用自定義model } else if ([propertyClassType isEqualToString:@"NSString"]) { //字符串類型 if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //自定義類型,循環(huán)調(diào)用,一直到不是自定義類型 propertyValue = [NSClassFromString(propertyClassType) yj_initWithDictionary:propertyValue]; if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } } else if ([propertyType isEqualToString:YJClassType_basic]) { //基本數(shù)據(jù)類型 if ([propertyClassType isEqualToString:@"c"]) { //bool類型 NSString *lowerValue = [propertyValue lowercaseString]; if ([lowerValue isEqualToString:@"yes"] || [lowerValue isEqualToString:@"true"]) { propertyValue = @(YES); } else if ([lowerValue isEqualToString:@"no"] || [lowerValue isEqualToString:@"false"]) { propertyValue = @(NO); } } else { propertyValue = [[[NSNumberFormatter alloc] init] numberFromString:propertyValue]; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //其他類型 } } free(arrPropertys); return myObj;}4、字典中包含一些iOS不能用的字段
有時候接口返回的數(shù)據(jù)中有id等iOS中和關(guān)鍵字重合的字段, 這個時候我們需要將id手動映射到模型中對應(yīng)的屬性中我們在剛剛創(chuàng)建的協(xié)議中在添加一個方法來處理@protocol YJModelDicTransform <NSObject>@optional/** * 數(shù)組中存儲的類型 * * @return key --- 屬性名, value --- 數(shù)組中存儲的類型 */+ (NSDictionary *)yj_objectClassInArray;/** * 替換一些字段 * * @return key -- 模型中的字段, value --- 字典中的字段 */+ (NSDictionary *)yj_propertykeyReplacedWithValue;@end在model中實現(xiàn)這個方法+ (NSDictionary *)yj_propertykeyReplacedWithValue{ return @{@"_id":@"id"};}+ (instancetype)yj_initWithDictionary:(NSDictionary *)dic{ id myObj = [[self alloc] init]; unsigned int outCount; //獲取類中的所有成員屬性 objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { objc_property_t property = arrPropertys[i]; //獲取屬性名字符串 //model中的屬性名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; //字典中的屬性名 NSString *newPropertyName; if ([self respondsToSelector:@selector(yj_propertykeyReplacedWithValue)]) { newPropertyName = [[self yj_propertykeyReplacedWithValue] objectForKey:propertyName]; } if (!newPropertyName) { newPropertyName = propertyName; } NSLog(@"屬性名:%@", propertyName); id propertyValue = dic[newPropertyName]; if (propertyValue == nil) { continue; } //獲取屬性是什么類型的 NSDictionary *dicPropertyType = [self propertyTypeFromProperty:property]; NSString *propertyClassType = [dicPropertyType objectForKey:@"classType"]; NSString *propertyType = [dicPropertyType objectForKey:@"type"]; if ([propertyType isEqualToString:YJClassType_object]) { if ([propertyClassType isEqualToString:@"NSArray"] || [propertyClassType isEqualToString:@"NSMutableArray"]) { //數(shù)組類型 if ([self respondsToSelector:@selector(yj_objectClassInArray)]) { id propertyValueType = [[self yj_objectClassInArray] objectForKey:propertyName]; if ([propertyValueType isKindOfClass:[NSString class]]) { propertyValue = [NSClassFromString(propertyValueType) yj_initWithArray:propertyValue]; } else { propertyValue = [propertyValueType yj_initWithArray:propertyValue]; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } } else if ([propertyClassType isEqualToString:@"NSDictionary"] || [propertyClassType isEqualToString:@"NSMutableDictionary"]) { //字典類型 不考慮,一般不會用字典,用自定義model } else if ([propertyClassType isEqualToString:@"NSString"]) { //字符串類型 if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //自定義類型 propertyValue = [NSClassFromString(propertyClassType) yj_initWithDictionary:propertyValue]; if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } } else if ([propertyType isEqualToString:YJClassType_basic]) { //基本數(shù)據(jù)類型 if ([propertyClassType isEqualToString:@"c"]) { //bool類型 NSString *lowerValue = [propertyValue lowercaseString]; if ([lowerValue isEqualToString:@"yes"] || [lowerValue isEqualToString:@"true"]) { propertyValue = @(YES); } else if ([lowerValue isEqualToString:@"no"] || [lowerValue isEqualToString:@"false"]) { propertyValue = @(NO); } } else { propertyValue = [[[NSNumberFormatter alloc] init] numberFromString:propertyValue]; } if (propertyValue != nil) { [myObj setValue:propertyValue forKey:propertyName]; } } else { //其他類型 } } free(arrPropertys); return myObj;}控制器中- (void)viewDidLoad { [super viewDidLoad]; self.title = @"字典轉(zhuǎn)模型"; NSDictionary *dicTest = @{@"id":@"121", @"name":@"張三", @"phone":@"110", @"age":@"10", @"user":@{@"userId":@"2"}, @"arrUsers":@[@{@"userId":@"2"},@{@"userId":@"2"},@{@"userId":@"2"}]}; TestModel *model = [TestModel yj_initWithDictionary:dicTest]; NSLog(@"model-----id:%@, name:%@, phone:%@, address:%@, age:%@, userId:%@, userName:%@", model._id, model.name, model.phone, model.address, @(model.age), model.user.userId, model.user.userName); [model.arrUsers enumerateObjectsUsingBlock:^(UserModel *obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"arrUser----userId:%@", obj.userId); }];}控制臺打印2016-12-19 17:17:03.245 YJModelDicTransform[2099:158162] model-----id:121, name:張三, phone:110, address:(null), age:10, userId:2, userName:(null)
2016-12-19 17:17:03.245 YJModelDicTransform[2099:158162] arrUser----userId:2
2016-12-19 17:17:03.245 YJModelDicTransform[2099:158162] arrUser----userId:2
2016-12-19 17:17:03.245 YJModelDicTransform[2099:158162] arrUser----userId:2
到此,基本完成了字典轉(zhuǎn)模型的功能。
新聞熱點
疑難解答