Key-Value Observing (Brief Intro)

Key-Value Observing

Key-value observing (KVO) is a mechanism that enables objects to be notified of changes to the properties of other objects. It is essentially an implementation of the observer software design pattern, and is implemented in numerous software libraries and frameworks. In fact, it is a key component of the model-view-controller (MVC) design pattern, a central component of the Cocoa and Cocoa Touch frameworks. Key-value observing provides numerous benefits, including the decoupling of observer objects from the object being observed (i.e., the subject), framework-level support, and a full-featured set of APIs. The following example uses key-value observing to register an Administrator object as an observer of the name property of a Person instance named person.

Administrator *admin = [Administrator new];
[person addObserver:admin
         forKeyPath:@"name"
            options:NSKeyValueObservingOptionNew
            context:NULL];

Invoking the addObserver:forKeyPath:options:context: method creates a connection between the subject and the observer object. When the value of the observed property changes, the subject invokes the observer’sobserveValueForKeyPath:ofObject:change:context: method. In this method, an observer class implements logic to handle the property change. A subject automatically invokes this method when the value of an observed property is changed in a KVO-compliant manner, or the key on which it depends is changed.Observers must implement this method for key-value observing to work properly.Listing 18-13 provides an example implementation of this method for theAdministrator class.

Listing 18-13.  Example Implementation of observeValueForKeyPath:ofObject:change:context: Method

@implementation Administrator

...
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
        change:(NSDictionary *)change context:(void *)context
{
  if ([@"name" isEqual:keyPath])
  {
    // Insert logic to handle update of person's name
    ....
  }
  // Invoke the superclass's implementation if it implements this!
  [super observeValueForKeyPath:keyPath
                       ofObject:object
                         change:change
                        context:context];
}
...

@end

The NSKeyValueObserving informal protocol defines the API for key-value observing. Its methods enable change notification, registering observers, notifying observers of changes, and customizing observing. NSObject provides the default implementation of the NSKeyValueObserving protocol, and thus any class that descends from NSObject has built-in support for key-value observing. In addition, you can further refine notifications by disabling automatic observer notifications and implementing manual notifications using the methods in this protocol.

Key-Value Observing or Notifications

In Chapter 12, you learned about the Foundation Framework notification classes(NSNotificationNSNotificationCenterNSNotificationQueue). These APIs provide a mechanism for passing information between objects, similar to key-value observing. Because this is somewhat similar to the function of key-value observing, you may be wondering, “How do these two mechanisms compare, and which do you use?” Well, there are a variety of differences between key-value observing and the notification APIs that will influence which you choose for the scenario in question, so I’ll compare the two here.

An NSNotification instance encapsulates generic information, so it supports a wide class of system events (including property changes), whereas key-value observing is strictly for notification of changes to properties of objects. As such, the KVO API is potentially simpler to use (vs. the notifications API) for property changes alone.

The notification classes employ a broadcast model of interaction, whereby information (encapsulated in an NSNotification object) is distributed via a centralized notification center (NSNotificationCenter instance). It allows a message to be sent to more than one object, and doesn’t even require receivers to be registered to support notifications. The notification classes support both synchronous and asynchronous (using an NSNotificationQueue instance) posting of notifications. Key-value observing, on the other hand, utilizes a point-to-point interaction model, whereby the subject directly sends a notification to registered observers when a property changes, and blocks until the corresponding method finishes execution. Both mechanisms provide loose coupling; however, the notification APIs also provide nonblocking (i.e., asynchronous) interactions, and thus have the potential to provide greater application responsiveness.

Because the notification mechanism completely decouples the sender and receiver of notification events, there is no mechanism for direct, two-way communication between the two; both must register for notifications in order to provide bidirectional communication. Key-value observing, on the other hand, enables the observer to send a message to the subject, because the subject is provided as a parameter to the observer’sobserveValueForKeyPath:ofObject:change:context: method.

A notification is distinguished by its name, and hence the name must be unique to ensure that posted notifications are received by the correct observer(s). Apple specifies a set of naming conventions in the Coding Guidelines for Cocoa document to minimize the possibility of notification name collisions. Because property names are specific to a class (i.e., have class namespace) and subjects are directly bound to observers, naming collisions are not a concern.

Key-Value Observing APIs

The NSKeyValueObserving protocol APIs provide methods to both add and remove observers for an input key path. Invoking theaddObserver:forKeyPath:options:context: method creates a connection between the subject and the observer object. Because this creates a strong reference to the observer, an object should be removed as an observer prior to the subject being deallocated. The removeObserver:forKeyPath: method performs this function. It stops an object from receiving change notifications for a property with the specified key path. The addObserver:forKeyPath:context: performs the same function relative to a receiver and a context. When the same observer is registered for the same key-path multiple times, but with different contexts, the context pointer can be used to determine which observer to remove. Listing 18-14demonstrates the use of these APIs to add and remove an Administrator object as an observer of the name property of a Person instance named person.

Listing 18-14.  KVO Adding and Removing an Observer

Administrator *admin = [Administrator new];
[person addObserver:admin
         forKeyPath:@"name"
            options:NSKeyValueObservingOptionNew
            context:NULL];
[person removeObsserver:admin forKeyPath:@"name"];

Key-value observing provides APIs (declared in the NSKeyValueObserving protocol) to support both automatic and manual key-value change notification. They enable an observer to perform logic both before and after a property value is changed. The APIs support attributes, to-one, and to-many relationship properties.

  • APIs for attributes and to-one relationship properties:
    • willChangeValueForKey:
    • didChangeValueForKey:
  • APIs for to-many, ordered relationship properties:
    • willChange:valuesAtIndexes:forKey:
    • didChange:valuesAtIndexes:forKey:
  • APIs for to-many, unordered relationship properties:
    • willChangeValueForKey:withSetMutation:usingObjects:
    • didChangeValueForKey:withSetMutation:usingObjects:

The default NSObject implementation automatically invokes the correct change notification methods on an observer when a corresponding property value is changed. Automatic/manual change notification is configured with the NSKeyValueObserving automaticallyNotifiesObserversForKey: method. The default NSObject implementation returns YES for this method, hence it performs automatic change notification. To perform manual change notification, your class must override the NSKeyValueObservingautomaticallyNotifiesObserversForKey: method. For properties, you want to perform manual change notification; the method should return NOIn this case, your code must invoke the NSKeyValueObserving protocol method willChange:before changing the property value, and the didChange: method after changing the value. Listing 18-15 provides an example implementation of manual change notification for the name property of the Person class, as shown in Figure 18-1.

Listing 18-15.  Example Implementation of KVO Manual Change Notification

- (void)changeName:(Name *)newName
{
  [self willChangeValueForKey:@"name"];
  self.name = newName;
  [self didChangeValueForKey:@"name"];
}

So why would you want to perform manual change notification? Reasons include more fine-grained control over the sending of change notifications, eliminating unnecessary notifications (e.g., cases when the new property value is the same as its old value), and grouping notifications.

The NSKeyValueObserving protocol also provides several methods for customizing observing. The keyPathsForValuesAffectingValueForKey: method retrieves the key paths for all properties whose values affect the value of the input key. This method is used to register dependent keys, when the value of one property depends upon that of one or more properties in another object. For to-one relationship properties, this method should be overridden to enable your code to return the appropriate set of dependent keys for the property. As an example, theAddress class, as shown in Figure 18-1, declares street , city , state, and zipproperties. A zip property depends on the other three properties; thus, an application observing the zip property should be notified if any of the other address properties changes. This can be accomplished by having the Address class override the method to return a collection of key paths consisting of the keys street , cityand state, as shown in Listing 18-16.

Listing 18-16.  Registering Dependent Keys for To-One Relationship Properties

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
  NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
  if ([key isEqualToString:@"zip"])
  {
    NSArray *affectingKeys = @[@"street", @"city", @"state"];
    keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
  }
   
  return keyPaths;
}

It is also possible to register dependent keys for to-one relationship properties by implementing a class method that follows the naming conventionkeyPathsForValuesAffecting<Key>, where <Key> is the name of the attribute that is dependent on the values. Comparable methods and techniques are provided to register dependent keys for to-many relationship properties.

The observationInfo and setObservationInfo: methods enable you to get/set observation information about all the observers that are registered with the subject.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值