KVO

KVO 基础

解释: Key-Value observing 就是某个对象的属性改变时通知其他对象的机制。对于被观察对象来说,键值观察(KVO)就是注册想要监视的属性的键路径和观察者

目的: 为了在GUI组件和程序之间调整值

过程: 当属性改变的时候,观察者就会接收到消息,虽然和 NSNotification 通知相似,但是 KVO 不存在通知中心的对象,所以不可以指定消息的选择器。

KVO 可以实现监视属性, 一对一的关系, 一对多关系等各种形式的属性,可以使用 NSObject 来实现的。

KVO 准则

使用 键值编码的准则 来访问 访问器 或者 实例变量 的情况下,才可以监视属性的变化。

在方法内直接改变实例变量的变量值是不能被监视的 > 实例验证代码

结果显示:

1
2
3
$ clang kvcsample.m -fobjc-arc -framework Foundation
$ ./a.out
$

并没有结果

准则具体如下

  1. 访问器方法而改变 - 这个时候访问器就是 @proeprty 所产生的 get 和 set 的方法名字。
  2. 使用 setValue:forKey: 和键进行改变。此时可能不经过访问器
  3. 使用 setValue:forKeyPath: 和键路径进行变化,也可能不经过访问器。不仅仅是最终的监视对象的属性,当路径中的属性发生变化时也会被通知。

KVO 的方法

  • 注册监视
    1
    2
    3
    4
    - (void) addObserver:(NSObject \*) anObserver
    forKeyPath:(NSString \*) keyPath
    options:(NSKeyValueObservingOptions) options
    context:(void *)context**

监视键路径 keyPath 中的某个属性,观察者anObserver,消息的发送者为被观察者

属性发生变化的时候,就会发送通知消息。

消息包含变化内容的字典数据,context 中指定的任意指针。

options 指定字典数据包含什么样的值。
NSKeyValueObservingOptionNew … 提供改变后的属性值
NSKeyValueObservingOptionOld … 提供改变前的属性值。

当属性改变的时候,下面的消息将会发送给观察者。

  • 获取监视通知
    1
    2
    3
    4
    - (void)observeValueForKeyPath:(NSString *) keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context

change 保存着改变的相关信息,context 返回注册观察者时指定的值。

如果要停止监视,实现一下方法

  • 停止监视

    1
    2
    - (void)removeObserver: (NSObject *) anObserver
    forKeyPath: (NSString *) keyPath
  • change 字典中保存的入口
    NSKeyValueChangeKindKey

    属性改变的种类
    

    NSKeyValueChangeNewKey

    属性的新对象的值
    

    NSKeyValueChangeOldKey

    属性旧对象的值
    

    NSKeyValueChangeIndexesKey

    动态数组中进行了插入,删除,置换。表示执行了这些操作的索引集合 --- NSIndexSet 类
    的实例为入口值
    

验证

用于验证的代码

结果显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
--- Received by <<Person, Jiro, HP=140>> ---
Object=<<Person, Taro, HP=800>>, Path=hitPoint
Change={
kind = 1;
new = 800;
old = 500;
}
--- Received by <<Dragon, Choco, HP=400>> ---
Object=<<Dragon, Choco, HP=400>>, Path=master.hitPoint
Change={
kind = 1;
new = 800;
old = 500;
}
--- Received by <<Dragon, Choco, HP=400>> ---
Object=<<Dragon, Choco, HP=400>>, Path=master.hitPoint
Change={
kind = 1;
new = 140;
old = 600;
}
--- Received by <<Person, Jiro, HP=140>> ---
Object=<<Person, Taro, HP=500>>, Path=hitPoint
Change={
kind = 1;
new = 500;
old = 600;
}
--- Received by <<Dragon, Choco, HP=400>> ---
Object=<<Dragon, Choco, HP=400>>, Path=master.hitPoint
Change={
kind = 1;
new = 340;
old = 140;
}

代码的结果分析
(1) 改变 p1 的 hitPoint,并确实向 p2 和 dra 发送了消息
(2) 虽然调用了更改 hitPoint 的消息,但是没有收到相应的消息
(3) 改变了 master ,收到了 master.hitPoint 的消息。即使不是指定的键路径的属性,在产生改变时也会被通知
(4),(5) 使用声明属性改变了 p2, p1 的 hitPoint 也可以被监视。
(6) 删除了通知后就收不到了。

名词解释

被观察者

1
属性值发生改变的时候,会被观察者了解到它的发生情况

观察者

1
设置观察对象的主动者