Objective-C Key-Value Observing 模式

来源:互联网 发布:人为什么追求卓越 知乎 编辑:程序博客网 时间:2024/05/22 14:18

一、简介

KVO,即Key-value observing,是一种允许某些 objects 观察其它 objects 指定属性值变化的机制。

为了理解 KVO,须先理解KVC,即key-value coding.

1、概述

KVO提供了一种允许观察者observer监控被观察者observed object指定属性值变化的机制。

这种机制对于程序中 model and controller 层之间的通讯特别有用. Controller对象观察model对象属性值的变化,而view对象通过controller对象观察model对象属性值的变化。另外,model对象也可以观察其它model对象(用以确定某个相关的属性值的变化)甚至它自己的(也用来确定某个相关的值的变化)。

你可以一对一和一对多的观察 properties 包括一些 simple attributes。只要监控的属性值发生变化,一对多的观察者会收到通知。

设置一个 property 的观察者(observer) 有以下三个步骤,这三个步骤也擅明了KVO的工作机制.

1、首先,看看 KVO是否在某个场景很有用,例如,某观察者需要收到另一个对象中某个指定的property值发生任何变化的通知。

                                 Art/kvo_objects.jpg

例如,一个 PersonObject 对象希望知道另一个 BankObject 对象中 accountBalance property 发生的任何变化.

2、该 PersonObject 对象必须注册为 BankObject’s accountBalance property 的观察者,通过发 addObserver:forKeyPath:options:context: 消息.

Art/kvo_objects_connection.jpg

注意:addObserver:forKeyPath:options:context:方法构建了观察者及被观察者之间的一种连接. 这种连接并不是构建于观察者及被观察者所属的两个类,而是所指定的观察者及被观察者对象.

3、为了对指定被察者的任何变化通知作出响应,观察者必须实现 observeValueForKeyPath:ofObject:change:context: 方法. 该方法的实现定义了观察者是如何响应指定被观察者的变化通知。 也是在这个方法中,你可以自定义对某个观察的属性值的变的响应。

如何注册并接收观察通知,参见“Registering for Key-Value Observing”。

4、当被观察的某项属性值发生变化,observeValueForKeyPath:ofObject:change:context:方法会以一种KVO兼容的方式被自动的调用.

Art/kvo_objects_notification.jpg

如何指定某key值的变化依赖于另一个key值的变化,参见“Registering Dependent Keys”

KVO最主要的便利之处在于,每当被观察的key值发生变化时,你不必自己去发送相应的通知。 这种良好定义的架构已经获取框架水平的支持,可以方便的采用。

自动和手动KVO之间的区别,以及如何实现这两者,参见“KVO Compliance”

不同于使用 NSNotificationCenter 产生的通知 ,KVO没有一个 central object 来为所有的观察者提供通知。相反,当被观察的key值发生变化后,通知会直接发送到观察者对象。NSObject类提供了KVO的基本实现,不需要重写这些方法。

KVO如何实现,参见“Key-Value Observing Implementation Details ”

二、KVO的注册

为了能收到某 property's key-value observing notifications,须实现以下三件事:

(1)被观察者类中希望被观察的属性,必须兼容KVO

(2)必须把观察者注册给被观察者,使用addObserver:forKeyPath:options:context:方法

(3)观察者类必须实现observeValueForKeyPath:ofObject:change:context:方法

注:并非所有的类的属性者都兼容KVO,通过遵循KVO兼容中描述的步骤来确保自己的类兼容KVO。

1、注册观察者

通过给被观察者发送addObserver:forKeyPath:options:context:消息,并把观察者及被观察的属性的key作为参数传递给它。options指定发送通知条件及提供给观察者的信息,详情见文档。NSKeyValueObservingOptionOld 指定提供 the original object value.  NSKeyValueObservingOptionNew 指定提供 the new value. 若想两个值都接收可以使用 NSKeyValueObservingOptionOld || NSKeyValueObservingOptionNew .

示例,注册inspector对象为openingBalance属性的观察者。

- (void)registerAsObserver {    /*     Register 'inspector' to receive change notifications for the "openingBalance" property of     the 'account' object and specify that both the old and new values of "openingBalance"     should be provided in the observe… method.     */    [account addObserver:inspector             forKeyPath:@"openingBalance"                 options:(NSKeyValueObservingOptionNew |                            NSKeyValueObservingOptionOld)                    context:NULL];}
context 参数,The context pointer 会提供给观察者 ,observeValueForKeyPath:ofObject:change:context: 当方法被调用时。 context pointer 可以是一个 C pointer、an object reference. context pointer 可被用作一个 unique identifier 来决定 the change that is being observed,或者提供 some other data to the observer。

注:KVO addObserver:forKeyPath:options:context方法让被观察者对观察者持有一种弱引用的关系。

2、接收被观察者属性key值变化的通知

当被观察的属性key值发生变化,观察者会收到 observeValueForKeyPath:ofObject:change:context: 消息。所有观察者必须实现该方法。

The observer 将收到与 the observer notification 关联的 object 和 key path,a change dictionary,以及 the context pointer (由 the observer 注册时提供).

The change dictionary entryNSKeyValueChangeKindKey 提供了 the type of change that occurred. 若 the observed object 的值发生变化,the NSKeyValueChangeKindKey entry 会返回 NSKeyValueChangeSetting. 取决于 the observer 注册时指定的 options 项,变更字典中 NSKeyValueChangeOldKey 及 NSKeyValueChangeNewKey 项包含了change前后的值. 若 the observed property 是一个对象,the value is provided directly.若 the observed property 是 a scalar or a C structure, the value is wrapped in an NSValue object (as with key-value coding).

若 the observed property 是一对多的关系,那么变更字典中 NSKeyValueChangeKindKey 项通过分别返回NSKeyValueChangeInsertion,NSKeyValueChangeRemoval,  NSKeyValueChangeReplacement 来表示该关系中一些 objects 是否被 inserted,removed,或者 replaced.

The change dictionary entry for NSKeyValueChangeIndexesKey 是一个 NSIndexSet object,它指定了发生改变的关系的索引 (indexes). 若某 observer 注册时,options 被指定为NSKeyValueObservingOptionNew 或 NSKeyValueObservingOptionOld ,则 the change dictionary 中的NSKeyValueChangeOldKey and NSKeyValueChangeNewKey entries 分别是一个包含变化前后相关对象值的 array (arrays containing the values of the related objects before, and after, the change).

下例演示了 inspector 类中,observeValueForKeyPath:ofObject:change:context: 方法的实现,影射了 openingBalance property 的新值及老值:

Listing 2  observeValueForKeyPath:ofObject:change:context 的实现:

- (void)observeValueForKeyPath:(NSString *)keyPath                      ofObject:(id)object                        change:(NSDictionary *)change                       context:(void *)context {     if ([keyPath isEqual:@"openingBalance"]) {        [openingBalanceInspectorField setObjectValue:            [change objectForKey:NSKeyValueChangeNewKey]];    }    /*     Be sure to call the superclass's implementation *if it implements it*.     NSObject does not implement the method.     */    [super observeValueForKeyPath:keyPath                         ofObject:object                           change:change                           context:context];}

3、删除观察者

可以通过向 the observed object 发送removeObserver:forKeyPath: 消息来删除一个 key-value observer,需指定 the observing object 及 the key path.

Listing 3  Removing the inspector as an observer of openingBalance

- (void)unregisterForChangeNotification {    [observedObject removeObserver:inspector forKeyPath:@"openingBalance"];}
若context参数是对象,那么你必须 keep a strong reference to it until removing the observer. 收到removeObserver:forKeyPath: 消息后,对于指定的 key path and object,观察者将再也不会接收任何observeValueForKeyPath:ofObject:change:context: 消息.

三、KVO兼容

为了使某 property 被认为是KVO-compliant,a class 必须确保实现以下几条:

(1)The class must be key-value coding compliant for the property, 参见“Ensuring KVC Compliance”。KVO与KVC支持同样的data types。
(2)The class 发送 KVO change notifications for the property。
(3)Dependent keys are registered appropriately (参见 “Registering Dependent Keys”.
0 0
原创粉丝点击