引言
该篇博客的思路是学习了以前看过的一篇博文,时间有点久,现在已经找不到了。算是借花献佛一把,与大家分享一下如何建立数据模型,并且实现当属性名与字典key值一致时,自动给属性赋值。
开始
还是先来讲讲思路吧。我们的目的是给模型传入数据,当模型中属性名与字典key一致时,属性的值就为字典key对应的值。要实现这点,最关键的就是如何匹配key与属性名,并且将value赋给属性。
上篇博客中,我们讲过利用runtime获取类中所有属性名,属性名获取到了,遍历字典的key,当属性名与key一致时,重写set方法,将key对应的value赋给属性,这样不就能实现了吗?
让我们开始动手实现吧
因为该数据模型不涉及UI,我们直接使用控制台新建工程。
1.创建测试使用的字典,模拟从服务器获得数据
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithCapacity:11];
//创建测试使用的字典
for (int i = 0; i < 11; i ++) {
NSString *key = [NSString stringWithFormat:@"boy%d",i];
NSString *value = [NSString stringWithFormat:@"i am the %i boy",i];
[data setValue:value forKey:key];
}
NSLog(@"%@",data);
输出结果:
2016-06-14 12:00:17.684 Runtime[10402:872011] Hello, World!
2016-06-14 12:00:17.686 Runtime[10402:872011] {
boy0 = “i am the 0 boy”;
boy1 = “i am the 1 boy”;
boy10 = “i am the 10 boy”;
boy2 = “i am the 2 boy”;
boy3 = “i am the 3 boy”;
boy4 = “i am the 4 boy”;
boy5 = “i am the 5 boy”;
boy6 = “i am the 6 boy”;
boy7 = “i am the 7 boy”;
boy8 = “i am the 8 boy”;
boy9 = “i am the 9 boy”;
}
笔者将在这个数据基础上为大家一步一步讲解如何动态绑定属性
2.创建数据模型基类BaseModel继承与NSObject
3.在.m中根据key值生成set方法和get方法
/**
* 根据Key值生成set方法:首先要编写的方法是传入一个字符串,然后返回该字符串所对应属性的setter方法。这个方法其实很简单的,就是把对应的字符串的首字母大写并且拼接上set关键字,再生产SEL并返回
*/
- (SEL)creatSetmethodWithPropertyName:(NSString *)propertyName {
//1.首字母大写
propertyName = propertyName.capitalizedString;
//2.拼接上set关键字
propertyName = [NSString stringWithFormat:@"set%@:",propertyName];
//返回set方法
return NSSelectorFromString(propertyName);
}
/**
* 通过字符串来创建该字符串的Getter方法,并返回
*/
- (SEL)creatGettermethodWithPropertyName:(NSString *)propertyName {
return NSSelectorFromString(propertyName);
}
4.通过runtime获取当前对象的所有属性名称
/**
* 通过运行时获取当前对象的所有属性名称,以数组的形式返回
*/
- (NSArray *)allPropertyName {
//存储所有的属性名称
NSMutableArray *allNames = [[NSMutableArray alloc] init];
//存储属性的个数
unsigned int propertyCount = 0;
//通过运行时获取当前类的属性
objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
//把属性放在数组中
for (int i = 0; i < propertyCount; i ++) {
//取出第一个属性
objc_property_t property = propertys[i];
const char *propertyName = property_getName(property);
[allNames addObject:[NSString stringWithUTF8String:propertyName]];
}
//释放
free(propertys);
return allNames;
}
返回的allNames即为所有属性名的数组
5.给模型添加外部调用接口
- (instancetype)initWithDictionary:(NSDictionary *)dic {
if (self = [super init]) {
[self assginToPropertyWithDictionary:dic];
}
return self;
}
+ (instancetype)modelWithDictionary:(NSDictionary *)dic {
return [[self alloc] initWithDictionary:dic];
}
6.在assginToPropertyWithDictionary:方法中将key对应的value赋给属性
- (void)assginToPropertyWithDictionary:(NSDictionary *)dic {
if (dic==nil) {
return;
}
//1.获取dic中所有的键
NSArray *keyAry = [dic allKeys];
//循环遍历字典的key,并且动态生成实体类的setter方法
for (int i = 0; i < keyAry.count; i ++) {
//通过上面的方法获取到每个属性的set方法
SEL setSel = [self creatSetmethodWithPropertyName:keyAry[i]];
if ([self respondsToSelector:setSel]) {
//获取字典中key所对应的value
NSString *value = [NSString stringWithFormat:@"%@",dic[keyAry[i]]];
//把值通过setter方法赋值给实体类属性
[self performSelectorOnMainThread:setSel withObject:value waitUntilDone:[NSThread isMainThread]];
}
}
}
7.运行时获取方法签名,外部可调用
/**
* 运行时获取方法签名
*/
- (void)displayCurrentModelProperty {
//获取实体类的属性名
NSArray *array = [self allPropertyName];
//拼接参数
NSMutableString *resultString = [[NSMutableString alloc] init];
for (int i = 0; i < array.count; i ++) {
//获取get方法
SEL getSel = [self creatGettermethodWithPropertyName:array[i]];
if ([self respondsToSelector:getSel]) {
//获取类和方法的签名
NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
//从签名获得调用对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置target
[invocation setTarget:self];
//设置select
[invocation setSelector:getSel];
//接收返回的值
NSObject * __unsafe_unretained returnValue = nil;
//调用
[invocation invoke];
//接收返回值
[invocation getReturnValue:&returnValue];
[resultString appendFormat:@"%@\n",returnValue];
}
}
NSLog(@"%@",resultString);
}
简陋版的数据模型基类就已经完成了。注意,该模型中必须要属性名与key一致才能赋值成功。先看看运用模型基类的效果吧
创建实体模型类继承于模型基类,添加属性,属性名与测试用的字典的key一致。
@interface BeautifulBoyModel : BaseModel
@property (nonatomic, copy)NSString *boy0;
@property (nonatomic, copy)NSString *boy1;
@property (nonatomic, copy)NSString *boy2;
@property (nonatomic, copy)NSString *boy3;
@property (nonatomic, copy)NSString *boy4;
@property (nonatomic, copy)NSString *boy5;
@property (nonatomic, copy)NSString *boy6;
@property (nonatomic, copy)NSString *boy7;
@property (nonatomic, copy)NSString *boy8;
@property (nonatomic, copy)NSString *boy9;
@property (nonatomic, copy)NSString *boy10;
在main文件中,调用基类初始化方法,传入测试用的字典
BeautifulBoyModel *model = [BeautifulBoyModel modelWithDictionary:data];
NSLog(@"%@",model.boy3);
[model displayCurrentModelProperty];
输出结果
2016-06-14 14:36:16.426 Runtime[10815:924734] i am the 3 boy
2016-06-14 14:48:50.723 Runtime[10815:924734] i am the 0 boy
i am the 1 boy
i am the 2 boy
i am the 3 boy
i am the 4 boy
i am the 5 boy
i am the 6 boy
i am the 7 boy
i am the 8 boy
i am the 9 boy
i am the 10 boy
大功告成!