键值观察

来源:互联网 发布:mac不能玩游戏吗2017 编辑:程序博客网 时间:2024/04/28 17:44

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。基于观察者注册的optionsNSKeyValueChangeOldKeyNSKeyValueChangeNewKey入口为属性更改前和更改后的值。如果其值为标量,会自动用NSValueNSNumber包裹。

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


Change字典的入口NSKeyValueChangeIndexesKey是一个NSIndexSet对象来指定更改的indexes。如果NSKeyValueObservingOptionVewNSKeyValueObservingOptionOld被注册,NSKeyValueChangeOldKeyNSKeyValueChangeNewKey入口是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]];

    }

   

    [super observeValueForKeyPath:keyPath

                         ofObject:object

                           change:change

                           context:context];

}

 

4,移除观察者对象:

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

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

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

5KVO 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来标识NSKeyValueChangeInsertionNSKeyValueChangeRemovalNSKeyValueChangeReplacement。受影响的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:

0 0