iOS 开发中,一旦涉及到网络请求,那么数据模型就不可少,当然你也可以选择直接用网络请求的数据,通过获取key
的方式来获取值,但是作为一个开发,我觉得的让后续接手的人能够快速明白你的代码,方便自己也方便别人。
下面,就来谈谈动态映射,针对json
格式的返回数据。
首先,先分析下思路吧
1.为了一劳永逸,我们需要一个基类,这里我定义了一个基类
GLCoderObject
2.通过什么方式将json
格式的数据转换为数据模型呢?
这里,我选择了setValuesForKeysWithDictionary
来实现,直接给模型的属性赋值
注意
:由于模型中可能有的key
并不一定需要,或者服务器增加了数据,那么在执行此方法的时候,就会抛出异常,那么怎么解决呢?
通过查看API,我们会发现一个函数
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
当key不存在时,会自动调用上面的这个方法,可以在这个方法中进行处理,通过重写此方法就可以避免
实现
子类应该怎么定义属性?属性应该和json
格式返回的数据一致,如下
//.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;//图片数组
@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>
//动态映射 将字典里的数据 赋值到模型
+ (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]];
}
/**
将对象转为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;
}
/**
针对对象里面属性的不同属性 进行转换
@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。在使用过程中,感觉还可以,如果有什么不对的,望各位大神多多指教。