键值观察 KVO

Key-Value Observing Programming Guide

1,注册Key-Value Observing: 要实现这个目的,需要:

1)被观察的类对你想要观察的属性必须是服从Key-Value observing的

2)你必须注册被观察对象的观察对象,使用addObserver:forKeyPath:options:context:.

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

重要提示:不是所有的类对所有的属性都服从KVO。你必须确保你拥有的类是服从KVO的

2,注册为一个观察者:使用addObserver:forKeyPath:options:context:.

使用选项NSKeyValueObservingOptionOld来指定初始对象值在change字典中提供给观察者。

使用NSKeyValueObservingOptionNew选项来通过change字典提供新值。

要项获得这两个值,需要按位OR这两个选项常量。

当你注册一个对象为观察者时,你还可以提供一个上下文指针。当observeValueForKeyPath:ofObject:change:context被调用时,上下文指针提供给观察者。上下文指针可以是一个C指针或一个对象引用。上下文指针可以被用来作为一个唯一的标识来确定被观察的更改,或提供其他一些数据给观察者。

提示:KVO addObserver:forKeyPath:options:context:方法不维系强引用到观察对象、被观察对象或上下文。你应该确保你维系强引用到观察、被观察、对象和上下文,如果必要的话。

3,接收更改通知:

当被观察的对象的属性更改时,观察者接收一个observeValueForKeyPath:ofObject:change:context:消息。所有的观察者必须实现这个方法。

Change字典入口NSKeyValueChangeKindKey提供发生的更改类型的信息。如果被观察对象的值发生了更改,NSKeyValueChangeKindKey入口返回NSKeyValueChangeSetting。基于观察者注册的options, NSKeyValueChangeOldKey和NSKeyValueChangeNewKey入口为属性更改前和更改后的值。如果其值为标量,会自动用NSValue和NSNumber包裹。

如果被观察的属性是一个多值属性,NSKeyValueChangeKindKey入口仍然指示对象是被添加、移除或被替换,通过返回NSKeyValueChangeInsertion、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement来标识。

Change字典的入口NSKeyValueChangeIndexesKey是一个NSIndexSet对象来指定更改的indexes。如果NSKeyValueObservingOptionVew或NSKeyValueObservingOptionOld被注册,NSKeyValueChangeOldKey和NSKeyValueChangeNewKey入口是arrays包含相关对象的更改前的值和更改后的值。

Listing 2  Implementation of 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];

}

4,移除观察者对象:

removeObserver:forKeyPath:消息给被观察对象。例子:

[observedObject removeObserver:inspector forKeyPath:@"openingBalance"];

如果上下文是一个对象,你必须对其保持一个强引用直到你移除观察者。

5,KVO Compliance:

1)类必须是符合KVC

2)类为属性发出KVO更改通知

3)基于的keys被适当地注册。

有两种机制来确保更改通知被发出。NSObject的自动支持并被默认可用于类所有遵循KVC的属性。

当通知被发出时,手动更改通知提供附加控制,并且需要额外的代码。你可以控制自动通知属性,通过子类化并实现automaticallyNotifiesObserversForKey:方法。

6,自动更改通知:

例如,下面的方法引发KVO更改通知发出。

//调用访问方法

[account setName:@"Savings"];

// 使用 setValue:forKey:.

[account setValue:@"Savings" forKey:@"name"];

// 使用 key path, where ‘account‘ is a kvc-compliant property of ‘document‘.

[document setValue:@"Savings" forKeyPath:@"account.name"];

// 使用 mutableArrayValueForKey: to retrieve a relationship proxy object.

Transaction *newTransaction = <#Create a new transaction for the account#>;

NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];

[transactions addObject:newTransaction];

7,手动更改通知:

一个实现手动更改通知的类必须实现automaticallyNotifiesObserversForKey:方法。它可能同时使用自动通知和手动通知。对于其要进行手动通知的属性,他应该在automaticallyNotifiesObserversForKey:方法中将其设置为NO。如下所示:

Listing 2  Example implementation of automaticallyNotifiesObserversForKey:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

BOOL automatic = NO;

if ([theKey isEqualToString:@"openingBalance"]) {

automatic = NO;

}

else {

automatic = [super automaticallyNotifiesObserversForKey:theKey];

}

return automatic;

}

要实现手动通知,在更改之前你调用willChangeValueForKey:,在更改之后调用didChangeValueForKey:。

Listing 3  Example accessor method implementing manual notification

- (void)setOpeningBalance:(double)theBalance {

[self willChangeValueForKey:@"openingBalance"];

_openingBalance = theBalance;

[self didChangeValueForKey:@"openingBalance"];

}

你可以最小化发送不必要的通知,通过首先检查值是否发生变化。

Listing 4  Testing the value for change before providing notification

- (void)setOpeningBalance:(double)theBalance {

if (theBalance != _openingBalance) {

[self willChangeValueForKey:@"openingBalance"];

_openingBalance = theBalance;

[self didChangeValueForKey:@"openingBalance"];

}

}

如果一个单独的操作引起多个键的值发生变化,你必须nest(筑巢)更改通知,像下面这样:

Listing 5  Nesting change notifications for multiple keys

- (void)setOpeningBalance:(double)theBalance {

[self willChangeValueForKey:@"openingBalance"];

[self willChangeValueForKey:@"itemChanged"];

_openingBalance = theBalance;

_itemChanged = _itemChanged+1;

[self didChangeValueForKey:@"itemChanged"];

[self didChangeValueForKey:@"openingBalance"];

}

对于一个有序的多值关联,除了指定更改的键,你还需要指定更改的类型和关联的indexes。更改的类型是一个NSKeyValueChange来标识NSKeyValueChangeInsertion、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement。受影响的indexes通过NSIndexSet被传递。

Listing 6  Implementation of manual observer notification in a to-many relationship

- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {

[self willChange:NSKeyValueChangeRemoval

valuesAtIndexes:indexes forKey:@"transactions"];

// Remove the transaction objects at the specified indexes.

[self didChange:NSKeyValueChangeRemoval

valuesAtIndexes:indexes forKey:@"transactions"];

}

8,注册Dependent Keys

有很多时候一个属性的值基于另外一个对象的的一个或多个属性。如果一个属性的值发生更改,然后衍生的属性也应该被标志为更改。你将如何确保在这些属性发生更改时,基于它的属性通过KVO通知被发布?

1)对于单一关联:

你需要重载keyPathsForValuesAffectingValueForKey:或实现一个合适的方法

例如,一个人的全名基于first name和last name。一个方法返回其全名:

- (NSString *)fullName {

return [NSString stringWithFormat:@"%@ %@",firstName, lastName];

}

一个应用观察fullName属性,必须在firstName和lastName属性发生变化时得到通知。

一个解决办法是重载keyPathsForValuesAffectingValueForKey:来指定fullName属性基于lastName和firstName。如下所示:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {

NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

if ([key isEqualToString:@"fullName"]) {

NSArray *affectingKeys = @[@"lastName", @"firstName"];

keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];

}

return keyPaths;

}

你的重载需要调用super的方法,并返回一个set。

你还可以得到同样的结果,通过实现一个类方法 keyPathsForValuesAffecting<Key>,<key>是属性的名字,第一个字母为大写,如下所示:

+ (NSSet *)keyPathsForValuesAffectingFullName {

return [NSSet setWithObjects:@"lastName", @"firstName", nil];

}

当你使用类别(category)向一个已存在的类添加一个计算出来的属性时,你不能重载keyPathsForValuesAffectingValueForKey:方法,因为你不能在类别中重载方法。在这种情况中,实现一个符合的keyPathsForValuesAffecting<Key>类方法来获得这种机制的好处。

2)对于多值关联:

keyPathsForValuesAffectingValueForKey:方法不支持包含多值关联的key-paths。例如,假设你有一个部门对象,有很多职员(employees),并且职员有工资属性。你可能想部门对象有一个totalSalary属性,基于所有职员的工资.你不能keyPathsForValuesAffectingTotalSalary并返回employees.salary。

这里有两种可能的解决方法:

a) 使用KVO来注册其父(这个例子里是部门)作为其所有子(这里是职员)对象的相关属性的观察者。你必须在添加或移除子对象时,添加或移除父对象作为观察者。在其observeValueForKeyPath:ofObject:change:context:方法中,你需要更新相应的值来响应通知,并发出通知。如下所示:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

if (context == totalSalaryContext) {

[self updateTotalSalary];

}

else

// deal with other observations and/or invoke super...

}

- (void)updateTotalSalary {

[self setTotalSalary:[self valueForKeyPath:@"[email protected]"]];

}

- (void)setTotalSalary:(NSNumber *)newTotalSalary {

if (totalSalary != newTotalSalary) {

[self willChangeValueForKey:@"totalSalary"];

_totalSalary = newTotalSalary;

[self didChangeValueForKey:@"totalSalary"];

}

}

- (NSNumber *)totalSalary {

return _totalSalary;

}

b) 如果你正在使用Core Data,你可以注册父对象到application’s notification center作为其管理的对象的上下文观察者。父对象应该响应子对象发布的相应的更改通知。

原文网址: http://supershll.blog.163.com/blog/static/37070436201263111521695/

时间: 2024-10-26 11:53:01

键值观察 KVO的相关文章

obj-c编程17:键值观察(KVO)

说完了前面一篇KVC,不能不说说它的应用KVO(Key-Value Observing)喽.KVO类似于ruby里的hook功能,就是当一个对象属性发生变化时,观察者可以跟踪变化,进而观察或是修正这个变化,这是通过回调观察者注册的回调函数来完成的.要使用键值观察,必须满足3个条件: 1 被观察对象必须对所观察属性使用符合KVC标准的存取器方法: 2 观察者必须实现接受通知的方法(回调方法):-observeValue:forKeyPath:ofObject:change:context:,该方法

Rx 键值观察KVO的使用

键值观察KVO的使用 1,KVO 介绍 KVO(键值观察)是一种 Objective-C 的回调机制,全称为:key-value-observing. 该机制简单来说就是在某个对象注册监听者后,当被监听的对象发生改变时,对象会发送一个通知给监听者,以便监听者执行回调操作. 2,RxSwift 中的 KVO RxCocoa 提供了 2 个可观察序列 rx.observe 和 rx.observeWeakly,它们都是对 KVO 机制的封装,二者的区别如下. (1)性能比较 rx.observe 更

KVO键值观察简述

KVO 键值观察,简单来说就是为一个key添加一个观察者,当key的值发生改变的时候会发送通知,在接到通知的时候会有回调方法被调用 #import "ViewController.h" @interface ViewController (){     NSMutableDictionary * myDict; } @end @implementation ViewController - (IBAction)dasdas:(id)sender {          //改变key的值

[深入浅出Cocoa]详解键值观察(KVO)及其实现机理

一,前言 Objective-C 中的键(key)-值(value)观察(KVO)并不是什么新鲜事物,它来源于设计模式中的观察者模式,其基本思想就是: 一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象.这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的.观察者模式较完美地将目标对象与观察者对象解耦. 在 Objective-C 中有两种使用键值观察的方式:手动或自动,此外还支持注册依赖键(即一个键依赖于其他键,其他键的变化也会作用到该键).下面将一一

KVO(键-值观察)

// 1.键-值观察 // 2.它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知. // 3.符合KVC(Key-ValuedCoding)机制的对象才可以使用KVO // 4.实现过程 // ①注册,指定被观察者 // ②实现回调方法 // ③移除观察 - (void)viewDidLoad {     [super viewDidLoad];     // Do any additional setup after loading the view from its nib.

K-V-C 键值观察机制

在两个不同的控制器之间传值是iOS开发中常有的情况,应对这种情况呢,有多种的应对办法.kvc就是其中的一种,所以,我们就在此解释之.   key value observing  键值观察,给人一种高冷的感觉,其实,我们可以用一个通俗的例子来解释之.就拿美俄之间的间谍来举例子.美俄是两个各自独立的国家,但是为了各自的利益,彼此之间勾心斗角,不断的爆出间谍丑闻.打住!从政治的深渊回到技术层面O(∩_∩)O.美国想知道俄罗斯的最新的导弹技术,于是派间谍收集情报,(kvo的第一步:注册观察者-美国,监

深度理解Key-Value Observing 键值观察

前言   在上一阶段的开发过程中,我们大量使用了 KVO 机制,来确保页面信息的及时同步.也因此碰到了很多问题,促使我们去进一步学习 KVO 的相关机制,再到寻找更好的解决方案.鉴于 KVO 让人欲仙欲死的使用经历,在这里做一个简单分享.此分享的目的,更多的是在于点出 KVO 相关的技术点,供我们大家在学习和使用过程中做一个参考. 对于 KVO 的背后机制感兴趣的同学,可以直接看第三部分,KVC 和 isa-swizzling . 对于 替代方案感兴趣的同学,请直接跳到末尾的第五部分,有列出了目

iOS 键值观察(KVO)简述及实例理解

KVO概述: KVO,即:Key-Value Observing,直译为:基于键值的观察者.  它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知. 简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了.KVO的优点: 当有属性改变,KVO会提供自动的消息通知.这样开发人员不需要自己去实现这样的方案:每次属性改变了就发送消息通知. 这是KVO机制提供的最大的优点.因为这个方案已经被明确定义,获得框架级支持,可以方便地采用. 开发人员不需要添加任何代码,

键-值观察

若想成为一个键的观察者,可添加如下代码. [theAppDelegate addObserver:self forKeyPath:@"fido" options:NSKeyValueObservingOptionOld context:nil]; 上述方法定义在NSObject中,实际上类似于说,“无论何时fido改变了就给我发个消息”,options和context决定fido改变时将哪些额外的数据与消息一起发送出去.触发方法过程如下 -(void)observeValueForKe