zoukankan      html  css  js  c++  java
  • 键值观察 KVO

    http://www.cnblogs.com/dyf520/p/3805297.html

    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:@"employees.@sum.salary"]];

    }

    - (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作为其管理的对象的上下文观察者。父对象应该响应子对象发布的相应的更改通知。

     
     
     
  • 相关阅读:
    导包路径
    django导入环境变量 Please specify Django project root directory
    替换django的user模型,mysql迁移表报错 django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependen cy user.0001_initial on database 'default'.
    解决Chrome调试(debugger)
    check the manual that corresponds to your MySQL server version for the right syntax to use near 'order) values ('徐小波','XuXiaoB','男','1',' at line 1")
    MySQL命令(其三)
    MySQL操作命令(其二)
    MySQL命令(其一)
    [POJ2559]Largest Rectangle in a Histogram (栈)
    [HDU4864]Task (贪心)
  • 原文地址:https://www.cnblogs.com/Cheetah-yang/p/4656254.html
Copyright © 2011-2022 走看看