KVC的使用
1、KVC 全称 key valued coding 键值编码
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制。ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC。
KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。
2、常用方法
获取值的方法
valueForKey:,传入NSString属性的名字。
valueForKeyPath:,传入NSString属性的路径,xx.xx形式。
valueForUndefinedKey它的默认实现是抛出异常,可以重写这个函数做错误处理。
那么问题来了 现在有连个类,Book类(对应属性:name,prices)和Person类(对应属性:name,height,Book *book),person有一本名字叫做《太阳的后裔》,那么现在想通过KVC给这本书赋值,应该如何操作?
个人总结答案:因为现在想修改的不是对应类也就是Person类的属性,是要修改Book的属性(person.book),所以需要通过valueForKeyPath来进行修改^_^
修改值的方法
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
setValue:forKey的搜索过程:
- 用@property定义的属性的key值(这里指的是property 系统生成的setter、getter方法 )
- setter方法的key值(用户自定义的setter方法)
- 直接访问成员变量,先找key,如果找不到,再找_key
- 以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
- 如果没有重写setValue:forUndefinedKey程序会马上崩溃。
注意:
1、使用KVC间接修改对象属性时,系统会自动判断对象属性的类型,并完成转换。
2、KVC可以访问成员变量,无论是否提供getter/setter方法,无论可见性是怎样,是否有readonly修饰。
setValue:forUndefinedKey与valueForUndefinedKey的应用
KVC的主要用途无非是ORM映射,就是将dictionary转换成model,但有些服务器返回的字段有可能是oc的关键字比如‘id’,’description’等。如上代码举得id的例子,我们无法让@property后面key值为id,于是使用大写的ID代替,KVC是区分大小写的我们不用担心。这时我们只需在setValue:forUndefinedKey把id的key值赋值给ID的key值,就可以避免关键字的尴尬。
3、dict <->model 互转
字典转模型
[self setValuesForKeysWithDictionary:dict];
模型转字典
[p dictionaryWithValuesForKeys:array];
4、KVC集合
NSArray/NSSet等都支持KVC
[array valueForKeyPath:@"dog.name"];
5、使用KVC计算属性
格式为:[p valueForKeyPath:@"Left keypath部分.@Collectionoperator部分.Right keypath部分”];
Left keypath部分:需要操作对象路径。
Collectionoperator部分:通过@符号确定使用的集合操作。
Right keypath部分:需要进行集合操作的属性。
举例:[p valueForKeyPath:@"books.@sum.price"];
@avg:平均值
@count:总数
@max:最大
@min:最小
@sum:总数
6、具体代码与实际编辑
1)当使用KVC给对象的属性赋值时,当根据输入的Key值没有找到对应的Value时调用
1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key { 2 3 NSLog(@"key = %@没有找到",key); 4 5 }
2)当找到了对应的key值,代码为
1 // 2 // main.m 3 // KVC熟悉与加强 4 // 5 // Created by ma c on 16/5/16. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 #import "Model.h" 12 13 int main(int argc, const char * argv[]) { 14 @autoreleasepool { 15 16 Model *model = [[Model alloc] init]; 17 18 [model setValue:@"Coder丶PSS" forKey:@"name"]; 19 [model setValue:@"178" forKeyPath:@"height"]; 20 21 // NSLog(@"%@",model); 22 23 NSLog(@"名字叫%@,身高为%@",[model valueForKey:@"name"],[model valueForKey:@"height"]); 24 25 26 27 } 28 return 0; 29 }
结果为:
3)使用原生方法解析数据
因为本人没有服务器,写了个假数据(plist),进行解析,效果是一样的
(1)plist:
(2)解析数据代码
model.m
1 // 2 // Model.h 3 // KVC熟悉与加强 4 // 5 // Created by ma c on 16/5/16. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Model : NSObject 12 13 @property (nonatomic, copy, readonly) NSString *name; 14 15 @property (nonatomic, assign, readonly) NSInteger height; 16 17 + (NSArray *)loadData; 18 19 @end
model.h
1 // 2 // Model.m 3 // KVC熟悉与加强 4 // 5 // Created by ma c on 16/5/16. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import "Model.h" 10 11 @implementation Model 12 13 - (NSString *)description 14 { 15 return [NSString stringWithFormat:@"model.name = %@, mdoel.height = %@", _name, @(_height)]; 16 } 17 18 - (void)setValue:(id)value forUndefinedKey:(NSString *)key { 19 20 NSLog(@"key = %@没有找到",key); 21 22 } 23 24 + (NSArray *)loadData { 25 26 //获取plist文件路径 27 NSString *path = @"/Users/mac/Desktop/KVC熟悉与加强/KVC熟悉与加强/CoderPSS.plist"; 28 29 //获取plist文件中的数组 30 NSArray *array = [NSArray arrayWithContentsOfFile:path]; 31 32 //初始化可变数组 33 NSMutableArray *dataList = [NSMutableArray array]; 34 35 //使用字典遍历plist中的数组 36 for (NSDictionary *dict in array) { 37 38 //初始化model类 39 Model *model = [[Model alloc] init]; 40 41 //使用KVC(setValuesForKeysWithDictionary)方法解析数据 42 [model setValuesForKeysWithDictionary:dict]; 43 44 //将model类加到可变数组 45 [dataList addObject:model]; 46 } 47 48 //返回可变数组 49 return dataList; 50 } 51 52 @end
运行结果