KVC - Key Value Coding 键值编码
一、KVC 简介
KVC 是一种可以直接通过字符串 key 来访问类属性的机制。而不是通过调用 Setter、Getter 方法访问。
二、Set
1.单个值 setValue:forkey:
// 只能给对象的直接属性赋值
// 结果等同 p.name = @"Nil Chou";
[p setValue:@"Nil Chou" forKey:@"name"];
2.多层赋值 setValue:forKeyPath
可以给对象的间接属性赋值
// 建议: 以后在开发中都使用setValue:forKeyPath:
[p setValue:@"Xiao Bai" forKeyPath:@"dog.name"];
3.给私有成员变量赋值
[p setValue:@"Nil Chou" forKey:@"_myName"];
[p setValue:@"Nil Chou" forKeyPath:@"_myName"]; // 推荐
4.字典转模型 - Notice
- 如果想使用 KVC 进行字典转模型,那么字典中的 key 必须和 Model 中的属性一模一样(个数 + 名称)。
否则报经典错误:this class is not key value coding-compliant for the key score.
。
处理方式:在 Model.m 中添加- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
防止程序崩溃。 - 如果使用 KVC 进行字典转模型,只能对当前调用 KVC 方法的对象进行转换,不能对它的属性的对象进行转换,意思就是说:对象中的对象无法转换,需要另行进行手动转换,再次处理。
- (instancetype)initWithDict:(NSDictionary *)dict{}
5.setValuesForKeysWithDictionary:
方法内部的实现原理
// 1.会拿到字典中的 key,然后根据这个 key 取出字典中的值,然后再根据这个 key 赋值给对象
// [p setValue:@"xxx" forKey:@"name"];
[p setValuesForKeysWithDictionary:dict];
三、Get
1.获取单个值
NSString *name = [p valueForKey:@"name"];
2.获取多层值
NSString *dogName = [p valueForKeyPath:@"dog.name"];
3.模型转字典
NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@"name", @"money"]];
4.获取数组中对象的值
Person *p1 = [Person new];
p1.name = @"Zhang San";
p1.money = 1111;
Person *p2 = [Person new];
p2.name = @"Li Si";
p2.money = 2222;
NSArray *arr = @[p1, p2];
// 如果数组中的元素都是同一种类型的数据, 可以使用KVC获取数组中所有对象的某个属性的值
NSArray *res = [arr valueForKeyPath:@"name"];
NSLog(@"res = %@", res);
// 获取去数组中name属性的所有值:Zhang San, Li Si
5.运算符
id res1 = [arr valueForKeyPath:@"@avg.money"];
// @avg.money:求出money的平均值
NSLog(@"res = %@", res1);
```
##四、Articles
- [KVC & KVO](http://www.jianshu.com/p/d5974566fd2f)
- [KVC(iOS)](http://www.jianshu.com/p/099f7af0e67a)
- [KVC](http://www.jianshu.com/p/099f7af0e67a)
#KVO - Key Value Observing 键值观察
##一、KVO 简介
KVO 是基于 runtime 机制实现的。当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的 setter 方法。派生类在被重写的 setter 方法内实现真正的通知机制。使用 KVO,可以方便地对指定对象的某个属性进行观察,当属性发生变化时,进行通知。
##二、KVO 三部曲
**1. 添加观察者 **
为了正确接受属性的变化通知,观察者对象必须先发一个消息给被观察者对象。
```
/**
* 由被观察者调用方法,添加观察者
*
* @param observer 观察者
* @param keyPath 观察哪一个属性
* @param options 设定通知观察者时传递的属性值,改变后:NSKeyValueObservingOptionNew,改变前:NSKeyValueObservingOptionOld
* @param context 一些其他的需要传递给观察者的上下文信息,通常设置为nil
*/
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
```
**2. 接受变化通知**
应该注意的是如果只是使用成员变量改变值的话是不会触发KVO的。要使用点语法,或者是KVC的方式改变值。
```
/**
* 一旦被观察的属性发生变化,系统就会调用这个方法,必须要实现的方法
*
* @param keyPath 监控的属性
* @param object 是我们 监听的对象
* @param change 里面包含了 keyPath 对应的新增
* @param context 一些其他的需要传递给观察者的上下文信息,通常设置为nil
*/
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context;
```
**3. 移除观察者**
在不需要观察时要进行移除,需要注意的是,在 ARC 和 MRC 下都要移除观察者,在 ARC 下不调用```[super dealloc]``` 。
```
/**
* 移除观察者
*
* @param observer 观察者
* @param keyPath 监控的属性
*/
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
```
##三、Example
**Code**
```
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
p.name = @"Nil Chou";
p.age = @18;
// 1.
[p addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:nil];
p.age = @24;
// 3.
[p removeObserver:self forKeyPath:@"age" context:nil];
}
// 2.
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context {
NSLog(@"keyPath = %@, object = %@ , change = %@, context = %@", keyPath, object, change, context);
// 对比 change 字典中 NSKeyValueChangeOldKey 与 NSKeyValueChangeNewKey,可判断监听属性的变化
NSNumber *oldAge = change[NSKeyValueChangeOldKey];
NSNumber *newAge = change[NSKeyValueChangeNewKey];
NSLog(@"oldAge = %@, newAge = %@",oldAge, newAge);
}
```
**Console**
```
2016-02-25 21:58:26.355 KVO[14298:3724330] keyPath = age, object = <Person: 0x7fe493695780> , change = {
kind = 1;
new = 24;
old = 18;
}, context = (null)
2016-02-25 21:58:26.355 KVO[14298:3724330] oldAge = 18, newAge = 24
```
##四、KVO 优劣
- 主要优点:每一次属性值改变都是自动发送通知,不需要开发者手动实现。
- 主要缺点:只能复写``` -observeValueForKeyPath:ofObject:change:context:``` 方法来获得通知,不能自定义 selector 来获取。也不能传递 block 等。
##五、Articles
- [KVO介绍](http://www.jianshu.com/p/0f3554e37141)
- [KVO(iOS)](http://www.jianshu.com/p/ed0422ffeeda)
- [KVO/NSNotification](http://www.jianshu.com/p/65ea732770eb)
- [Key-Value Observing](http://nshipster.cn/key-value-observing/)
- [iOS开发-KVO的奥秘](http://www.jianshu.com/p/742b4b248da9)
网友评论