iOS 開發(fā)中,一旦涉及到網(wǎng)絡請求,那么數(shù)據(jù)模型就不可少,當然你也可以選擇直接用網(wǎng)絡請求的數(shù)據(jù),通過獲取key的方式來獲取值,但是作為一個開發(fā),我覺得的讓后續(xù)接手的人能夠快速明白你的代碼,方便自己也方便別人。
下面,就來談談動態(tài)映射,針對json格式的返回數(shù)據(jù)。
首先,先分析下思路吧
1.為了一勞永逸,我們需要一個基類,這里我定義了一個基類
GLCoderObject
2.通過什么方式將json格式的數(shù)據(jù)轉(zhuǎn)換為數(shù)據(jù)模型呢?
這里,我選擇了setValuesForKeysWithDictionary來實現(xiàn),直接給模型的屬性賦值
注意:由于模型中可能有的key并不一定需要,或者服務器增加了數(shù)據(jù),那么在執(zhí)行此方法的時候,就會拋出異常,那么怎么解決呢?
通過查看API,我們會發(fā)現(xiàn)一個函數(shù)
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
當key不存在時,會自動調(diào)用上面的這個方法,可以在這個方法中進行處理,通過重寫此方法就可以避免
實現(xiàn)
子類應該怎么定義屬性?屬性應該和json格式返回的數(shù)據(jù)一致,如下
//.h文件
#import "GLCoderObject.h"
@interface DeviceDetailModel : GLCoderObject
@property (nonatomic, copy) NSString *building_id;//樓棟id
@property (nonatomic, copy) NSString *building_name;//樓棟名字
@property (nonatomic, copy) NSString *device_addr;//設備地址
@property (nonatomic, strong) NSArray *pict_file_list;//圖片數(shù)組
@end
//.m文件
#import "DeviceDetailModel.h"
#import "DevicePicListModel.h"
@implementation DeviceDetailModel
- (void)setPict_file_list:(NSArray *)pictfilelist
{
NSMutableArray *reArray = [NSMutableArray arrayWithCapacity:pictfilelist.count];
for (NSDictionary *dic in pictfilelist)
{
DevicePicListModel*record = [DevicePicListModel modelObjectWithDictionary:dic];
[reArray addObject:record];
}
_pict_file_list = [reArray copy];
}
@end
//=====DevicePicListModel
//.h文件
#import "GLCoderObject.h"
@interface DevicePicListModel : GLCoderObject
@property (nonatomic,copy) NSString *pict_file_id;//圖片id
@end
//.m文件
#import "DevicePicListModel.h"
@implementation DevicePicListModel
@end
潛在bug
如果在返回的json中,有關鍵字id,description
應當在子類中,重新定義一個屬性,如m_id,m_description,然后子類中重載父類方法
+(id)modelObjectWithDictionary:(NSDictionary *)dict
{
id obj = [super modelObjectWithDictionary:dict];
[obj setM_id:dict[@"id"] ? : @""];
[obj setM_description:dict[@"description"] ? : @""];
return obj;
}
貼上基類源碼
//.h文件
#import <Foundation/Foundation.h>
@interface GLCoderObject : NSObject<NSCoding, NSCopying>
//動態(tài)映射 將字典里的數(shù)據(jù) 賦值到模型
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
@end
//.m文件
#import "GLCoderObject.h"
#import <objc/runtime.h>
@implementation GLCoderObject
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
{
id obj = [[self alloc] init];
[obj setValuesForKeysWithDictionary:dict];
return obj;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}
//歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder
{
unsigned int count;
Ivar* ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char* name = ivar_getName(ivar);
NSString* strName = [NSString stringWithUTF8String:name];
id value = [self valueForKey:strName];
[aCoder encodeObject:value forKey:strName];
}
free(ivars);
}
//解檔
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++)
{
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *strName = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:strName];
[self setValue:value forKey:strName];
}
free(ivars);
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
return nil;
}
/**
描述信息
@return 返回描述信息 利于我們在debug 的時候方便查看
*/
- (NSString *)description
{
return [NSString stringWithFormat:@"%@",[self getObjectData:self]];
}
/**
將對象轉(zhuǎn)為NSDictionary
@param obj 對象
@return 返回的NSDictionary
*/
- (NSDictionary*)getObjectData:(id)obj
{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
unsigned int propsCount;
objc_property_t *props = class_copyPropertyList([obj class], &propsCount);
for(int i = 0;i < propsCount; i++)
{
objc_property_t prop = props[i];
NSString *propName = [NSString stringWithUTF8String:property_getName(prop)];
id value = [obj valueForKey:propName];
if(value == nil)
{
value = [NSNull null];
}
else
{
value = [self getObjectInternal:value];
}
[dic setObject:value forKey:propName];
}
return dic;
}
/**
針對對象里面屬性的不同屬性 進行轉(zhuǎn)換
@param obj 對象
@return 返回
*/
- (id)getObjectInternal:(id)obj
{
if([obj isKindOfClass:[NSString class]]
|| [obj isKindOfClass:[NSNumber class]]
|| [obj isKindOfClass:[NSNull class]])
{
return obj;
}
if([obj isKindOfClass:[NSArray class]])
{
NSArray *objarr = obj;
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:objarr.count];
for(int i = 0;i < objarr.count; i++)
{
[arr setObject:[self getObjectInternal:[objarr objectAtIndex:i]] atIndexedSubscript:i];
}
return arr;
}
if([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary *objdic = obj;
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:[objdic count]];
for(NSString *key in objdic.allKeys)
{
[dic setObject:[self getObjectInternal:[objdic objectForKey:key]] forKey:key];
}
return dic;
}
return [self getObjectData:obj];
}
@end
在上述源碼中,我還用到了歸檔,這在我們緩存的時候可以用到,如果需要用歸檔的方式進行緩存,個人愛好而已。
寫的順序有點亂,因為比較簡單,所以就不貼demo了,以上所有代碼就是一份完整的demo。在使用過程中,感覺還可以,如果有什么不對的,望各位大神多多指教。