以前KVC只是简单会用,但不了解KVC的真正强大,突然觉得,现在能在功能上都实现,但却不懂一些机制的特性,从此我在慢慢捡回来,发现了解一些原理还是蛮有趣的。今天学习下KVC机制。
一、简介
KVC key valued coding 键值编码
KVC通过键值间接编码
如果想要设置类的私有变量的值,开通通过KVC来设置和获取
KVC提供了一种在运行时而非编译时动态访问对象属性与成员变量的方式,该方法不需要调用get和set方法和变量实例就可以访问对象,KVC默认的实现方法有NSObject提供
关键方法定义在:NSKeyValueCoding Protocol
KVC支持类对象和内建基本数据类型
在iOS中,通过KVC可以直接用字符串的名字(key)来访问类属性的机制。而不是通过调用Setter/Getter方法访问
KVC是KVO、Core Data、CocoaBindings的技术基础,他们都是利用了OC的动态性
说明:访问变量的几种方式
1、设置public
2、利用属性访问
3、利用KVC,即使该属性是private也可以访问
二、KVC的使用
1、 代码示例 KVC访问:
如:Person类中 name 和 age 都是私有的,外面不能通过创建对象来访问。
如果想要设置和获取Person类中 name 和 age 的值 使用KVC 一步到位
// 修改值
[person setValue:@"张三" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:16] forKey:@"age"];
// 获取值
NSLog(@"%@", [person valueForKey:@"name"]); // 打印张三
// 除了通过键设置值外,键值编码还支持指定路劲,像文件系统一样,用“点”号隔开。如:Person类创建了一个Book类的属性 如果此刻通过Person类来访问Book类的实例变量 可以使用 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
Book *book = [[Book alloc] init];
// [book setValue:@"iOS" forKey:@"bookName"];
person.book = book;
[person setValue:@"iPhone" forKeyPath:@"book.bookName"]; // 用点号 book.bookName
// 如果找不到key的时候会奔溃,所以当找不到key的时候 可以会调用这个方法。 它的默认实现是抛出异常,可以重写这个函数做错误处理
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"没有找到key");
}
2、修改值
valueForKey: ; 传入NSString属性的名字
valueForKeyPath: ; 传入NSString属性的路劲,xx.xx形式 如@"book.bookName"
valueForUndefinedKey: ;它的默认实现是抛出异常,可以重写这个函数做错误处理
3、获取值
setValue:forKey:
setValue:forKeyPayh:
setValue:forUndefinedKey:
setNilValueFoeKey: 当对非类对象属性设置nil时,调用,默认抛出异常
三、KVC的实现细节
搜索Setter/Getter方法 ,KVC方法的实现get/set方法及实例变量的访问。
a、setValue:forKey是如何访问属性值的
1、检查是否存在set:方法
如果成员用@property,@synthsize处理,因为@synthsize告诉编译器自动生成set:格式的set方法,所以这种情况下会直接搜索到。
2、检查名为-_,-_is(只针对布尔值有效),-_set:方法
那么按_, _is, , is的顺序搜索成员名。
3、直接访问实例变量。实例变量可以是名为: 和 _;
4、如果找到设置成员的值,没有找到调用setValue:forUndefinedKey
b、valueForKey是如何访问属性值的
1、检查是否存在get, , is 方法
如果成员用@property,@synthsize处理,因为@synthsize告诉编译器自动生成get格式的get方法,所以这种情况也会直接搜索到。
2、如果getter没有找到,查找countOf, objectInAtindex: , AtIndexes格式的方法
3、2没有查到,那么查找countOf、enumeratorOf、memberOf:格式的方法
3、如果还没有查到,再检查类方法accessInstanceVariablesDirectly(默认返回YES),那么如果类方法accessInstanceVariablesDirectly返回YES,那么按_,_is,,is的顺序直接搜索成员名
4、如果找到返回这个值,没有找到调用valueForUndefinedKey。
注:如果不想让某个类禁用KVC里,那么重写 +(BOOL)accessInstanceVariablesDirectly方法 返回NO即可,默认是YES
c、KVC与容器
d、KVC与字典
四、KVC的使用
1、动态的取值和设置
// 修改值
[person setValue:@"张三" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:16] forKey:@"age"];
// 获取值
NSLog(@"%@", [person valueForKey:@"name"]); // 打印张三
2、访问和修改私有变量
// 修改值 name 和 age 都是私有变量
[person setValue:@"张三" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:16] forKey:@"age"];
// 获取值
NSLog(@"%@", [person valueForKey:@"name"]); // 打印张三
3、Model和字典转换
//看着熟悉吧
+ (YJUserInfoModel *)userInfoModelWithDictionary:(NSDictionary *)dictionary
{
YJUserInfoModel *userInfoModel = [[YJUserInfoModel alloc] init];
[userInfoModel setValuesForKeysWithDictionary:dictionary]; // 直接转换
userInfoModel.isBindCard = [NSString stringWithFormat:@"%@", [dictionary objectForKey:@"isBindCard"]];
return userInfoModel;
}
4、修改一些控件的内部属性
5、操作集合
6、函数操作集合
a、简单集合运算符
// 简单集合运算符共有@avg, @count, @max, @min, @sum 5种,暂不支持自定义
Book *book1 = [Book new];
book1.price = 22;
Book *book2 = [Book new];
book2.price = 22;
Book *book3 = [Book new];
[book3 setValue:[NSNumber numberWithFloat:33] forKey:@"price"];
// book3.price = 33;
Book *book4 = [Book new];
[book4 setValue:[NSNumber numberWithFloat:44] forKey:@"price"];
// book4.price = 44;
//
NSArray *arrBooks = @[book1, book2, book3, book4];
NSNumber *sum = [arrBooks valueForKeyPath:@"@sum.price"];
NSLog(@"sum=%f", [sum floatValue]);
//
NSNumber *avg = [arrBooks valueForKeyPath:@"@avg.price"];
NSLog(@"avg=%f", [avg floatValue]);
NSNumber *count = [arrBooks valueForKeyPath:@"@count"];
NSLog(@"count=%f", [count floatValue]);
NSNumber *min = [arrBooks valueForKeyPath:@"@min.price"];
NSLog(@"min=%f", [min floatValue]);
NSNumber *max = [arrBooks valueForKeyPath:@"@max.price"];
NSLog(@"max=%f", [max floatValue]);
// 打印结果
/**
2016-08-25 19:31:27.423 KVCAndKVODemo[6167:566030] sum=110.000000
2016-08-25 19:31:27.423 KVCAndKVODemo[6167:566030] avg=27.500000
2016-08-25 19:31:27.423 KVCAndKVODemo[6167:566030] count=4.000000
2016-08-25 19:31:27.423 KVCAndKVODemo[6167:566030] min=11.000000
2016-08-25 19:31:27.423 KVCAndKVODemo[6167:566030] max=44.000000
*/
b、对象运算符
// @distinctUnionOfObjects
// @unionOfObjects
// 返回值都是NSArray, 区别是前者返回的元素都是唯一,是去重以后的结果;后者返回的元素是全集。
NSLog(@"distinctUnionOfObjects");
NSArray *arrDistinct = [arrBooks valueForKeyPath:@"@distinctUnionOfObjects.price"];
for (NSNumber *price in arrDistinct) {
NSLog(@"%f", price.floatValue);
}
NSLog(@"unionOfObjects");
NSArray *arrUnion = [arrBooks valueForKeyPath:@"@unionOfObjects.price"];
for (NSNumber *price in arrUnion) {
NSLog(@"%f", price.floatValue);
}
//打印结果
/**
2016-08-25 19:39:04.612 KVCAndKVODemo[6207:570042] distinctUnionOfObjects
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 44.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 33.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 22.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] unionOfObjects
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 22.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 22.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 33.000000
2016-08-25 19:39:04.613 KVCAndKVODemo[6207:570042] 44.000000
*/
五、小结
当然KVC的强大还远远不止这些,这也算给自己的一个知识巩固。不过实际项目中对于访问属性还是比较少,因为如果设置成私有的自然就不希望外部访问。反之则可以直接使用set/get方法。当然修改一些控件的内部属性还是很有用的。具体还是看实际项目。最近都在巩固知识,发现要学的很多,学无止境
下篇学习KVO
demo:下载:http://download.csdn.net/detail/yj229201093/9613090
六、引用
感谢各路大神的博客,没有你们的优秀的博客,我也不会学习的那么快。
http://www.jianshu.com/p/ddff26a58172
http://ios.jobbole.com/84954/