在日常的开发中我们经常遇到的一种情况就是把从服务端请求回来的数据(此处暂且就当做是json的数据格式)映射成本地数据模型的对象。如果一个对象一个对象的去初始化,势必很浪费时间,这里是自己总结的一点想法:运用iOS的强大的运行时(runtime)来对这项工作做一个较统一的处理,废话不多说直接上代码。
1、首先声明一个基类PHObject代码如下
PHObject.h
@interface PHObject : NSObject<NSCopying,NSCoding>
// 解析API返回的JSON,返回对应的Model
- (id)initWithDictionary:(NSDictionary *)JSON;
// JSON key到property的映射关系(支持字典嵌套)
- (NSDictionary *)JSONKeysToPropertyKeys;
// JSON key到 array property的映射关系
- (NSDictionary *)JSONKeysToArrayPropertyKeys;
PHObject.m(此处尚未实现NSCopying和NSCoding协议)
//https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
#import "PHObject.h"
#import <objc/runtime.h>
@implementation PHObject
// 解析API返回的JSON,返回对应的Model对象
- (id)initWithDictionary:(NSDictionary *)JSON{
if(self = [super init]){
[JSON enumerateKeysAndObjectsUsingBlock:^(id JSONKey,id JSONObj ,BOOL *stop){
NSString* propertyKey = [self JSONKeysToPropertyKeys][JSONKey];
propertyKey = propertyKey ? propertyKey : JSONKey;
Class classOfProperty = [self propertyClassFromClass:[self class]
forPropertyKey:propertyKey];
if(classOfProperty != NULL){
id propertyValue = nil;
if([classOfProperty isSubclassOfClass:[PHObject class]] &&
[JSONObj isKindOfClass:[NSDictionary class]]){
propertyValue = [[classOfProperty alloc] initWithDictionary:JSONObj];
}
else if([classOfProperty isSubclassOfClass:[NSArray class]] &&
[JSONObj isKindOfClass:[NSArray class]]){
propertyValue = [self arrayWithArray:JSONObj
itemClassString:[self JSONKeysToArrayPropertyKeys][JSONKey]];
}
else{
propertyValue = JSONObj;
}
[self setValue:propertyValue forKey:propertyKey];
}
}];
}
return self;
}
//输出字符串的格式有待完善优化
- (NSString*)description
{
NSMutableDictionary* descDict = [[NSMutableDictionary alloc] init];
unsigned int outCount;
objc_property_t* propertylist = class_copyPropertyList([self class], &outCount);
for(int idx = 0; idx < outCount; ++idx){
objc_property_t property = *(propertylist + idx);
const char* propertyNameOfChar = property_getName(property);
if(propertyNameOfChar != NULL){
NSString* propertyName = [NSString stringWithUTF8String:propertyNameOfChar];
id propertyValue = [self valueForKey:propertyName];
[descDict setObject:propertyValue forKey:propertyName];
}
}
return [descDict description];
}
// JSON key到property的映射关系
- (NSDictionary *)JSONKeysToPropertyKeys{
return nil;
}
// JSON key到 array property的映射关系
- (NSDictionary *)JSONKeysToArrayPropertyKeys
{
return nil;
}
//NSCopying协议实现
- (id)copyWithZone:(NSZone *)zone{
id obj = [[[self class] allocWithZone:zone] init];
return obj;
}
// NSCoding协议实现
- (id)initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
if(aDecoder){
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
if(aCoder){
}
}
#pragma mark private method
//从类的属性获取对应属性的类
- (Class)propertyClassFromClass:(Class)objClass
forPropertyKey:(NSString*)propertyKey
{
//获取对应key的属性
objc_property_t property = class_getProperty(objClass, propertyKey.UTF8String);
//获取对应属性的类型
const char* propertyTypeOfChar = property_copyAttributeValue(property, "T");
NSString* propertyType = nil;
if(propertyTypeOfChar != NULL){
propertyType = [NSString stringWithUTF8String:propertyTypeOfChar];
propertyType = [propertyType stringByReplacingOccurrencesOfString:@"@" withString:@""];
propertyType = [propertyType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
free((void*)propertyTypeOfChar);
}
return NSClassFromString(propertyType);
}
//从json数组提取对应对象的数组
- (NSArray*)arrayWithArray:(NSArray *)array itemClassString:(NSString*)itemClassString
{
NSMutableArray* resultArray = [[NSMutableArray alloc] init];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
id itemObj = nil;
Class itemClass = NSClassFromString(itemClassString);
if([obj isKindOfClass:[NSDictionary class]] &&
[itemClass isSubclassOfClass:[PHObject class]]){
itemObj = [[itemClass alloc] initWithDictionary:obj];
}
else{
itemObj = obj;
}
[resultArray addObject:itemObj];
}];
return resultArray;
}
@end
2、第一点就是基类的声明和实现,接下来只需要所有的数据模型都是继承自PHObject,那么我们的json数据映射本地数据模型对象这项工作就会变的轻而易举。下面举一个例子:
PHUser.h
#import "PHObject.h"
@interface PHUser : PHObject
@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSNumber* age;
@property (nonatomic, assign) BOOL sex;
PHUser.m
#import "PHUser.h"
@implementation PHUser
@synthesize name = _name;
@synthesize age = _age;
@synthesize sex = _sex;
- (NSDictionary *)JSONKeysToPropertyKeys{
return @{@"name":@"name",
@"age":@"age",
@"sex":@"sex"};
}
@end
PHReport.h
#import "PHObject.h"
@class PHUser;
@interface PHReport : PHObject
@property (nonatomic, strong) PHUser* author;
@property (nonatomic, strong) NSString* title;
@property (nonatomic, strong) NSString* date;
@property (nonatomic, strong) NSArray* users;
PHReport.m
#import "PHReport.h"
@implementation PHReport
@synthesize author = _author;
@synthesize title = _title;
@synthesize date = _date;
@synthesize users = _users;
- (NSDictionary *)JSONKeysToPropertyKeys{
return @{@"author":@"author",
@"title":@"title",
@"date":@"date",
@"users":@"users"};
}
- (NSDictionary *)JSONKeysToArrayPropertyKeys
{
return @{@"users":@"PHUser"};
}
上面声明两个类PHUser和PHReport分别继承自PHObject
下面通过一个json字典来实例化一个PHReport对象
PHReport * obj = [[PHReport alloc] initWithDictionary:@{
@"author":@{
@"name":@"leon",
@"age":@25,
@"sex":@YES
},
@"users":@[@{@"name":@"leon1",
@"age":@22,
@"sex":@YES
},
@{@"name":@"leon1",
@"age":@22,
@"sex":@YES
},
@"testCount"],
@"title":@"标题",
@"date":@"日期"
}];
NSLog(@"\n%@",obj);
结果: