Ask anyone who’s been around the NSBlock a few times: Key-Value Observing has the worst API in all of Cocoa. It’s awkward, verbose, and confusing. And worst of all, its terrible API belies one of the most compelling features of the framework.

When dealing with complicated, stateful systems, dutiful book-keeping is essential for maintaining sanity. Lest the left hand not know what the right hand doeth, objects need some way to publish and subscribe to state changes over time.

In Objective-C and Cocoa, there are a number of ways that these events are communicated, each with varying degrees of formality and coupling:

NSNotification & NSNotification Center provide a centralized hub through which any part of an application may notify and be notified of changes from any other part of the application. The only requirement is to know what to look for, specifically in the name of the notification. For example, UIApplication Did Receive Memory Warning Notification signals a low memory environment in an application.

& provide a centralized hub through which any part of an application may notify and be notified of changes from any other part of the application. The only requirement is to know what to look for, specifically in the name of the notification. For example, signals a low memory environment in an application. Key-Value Observing allows for ad-hoc, evented introspection between specific object instances by listening for changes on a particular key path. For example, a UIProgress View might observe the number Of Bytes Read of a network request to derive and update its own progress property.

allows for ad-hoc, evented introspection between specific object instances by listening for changes on a particular key path. For example, a might observe the of a network request to derive and update its own property. Delegates are a popular pattern for signaling events over a fixed set of methods to a designated handler. For example, UIScroll View sends scroll View Did Scroll: to its delegate each time its scroll offset changes.

are a popular pattern for signaling events over a fixed set of methods to a designated handler. For example, sends to its delegate each time its scroll offset changes. Callbacks of various sorts, whether block properties like NSOperation -completion Block , which trigger after is Finished == YES , or C function pointers passed as hooks into functions like SCNetwork Reachability Set Callback(3) .

Of all of these methods, Key-Value Observing is arguably the least well-understood. So this week, NSHipster will endeavor to provide some much-needed clarification and notion of best practices to this situation. To the casual observer, this may seem an exercise in futility, but subscribers to this publication know better.

<NSKey Value Observing> , or KVO, is an informal protocol that defines a common mechanism for observing and notifying state changes between objects. As an informal protocol, you won’t see classes bragging about their conformance to it (it’s just implicitly assumed for all subclasses of NSObject ).

The main value proposition of KVO is rather compelling: any object can subscribe to be notified about state changes in any other object. Most of this is built-in, automatic, and transparent.

For context, similar manifestations of this observer pattern are the secret sauce of most modern Javascript frameworks, such as Backbone.js and Ember.js.

Subscribing

Objects can have observers added for a particular key path, which, as described in the KVC operators article, are dot-separated keys that specify a sequence of properties. Most of the time with KVO, these are just the top-level properties on the object.

The method used to add an observer is –add Observer:for Key Path:options:context: :

- ( void ) add Observer :( NSObject * ) observer for Key Path :( NSString * ) key Path options :( NSKey Value Observing Options ) options context :( void * ) context

observer : The object to register for KVO notifications. The observer must implement the key-value observing method observeValueForKeyPath:ofObject:change:context:.

: The object to register for KVO notifications. The observer must implement the key-value observing method observeValueForKeyPath:ofObject:change:context:. key Path : The key path, relative to the receiver, of the property to observe. This value must not be nil .

: The key path, relative to the receiver, of the property to observe. This value must not be . options : A combination of the NSKey Value Observing Options values that specifies what is included in observation notifications. For possible values, see “NSKeyValueObservingOptions”.

: A combination of the values that specifies what is included in observation notifications. For possible values, see “NSKeyValueObservingOptions”. context : Arbitrary data that is passed to observer in observe Value For Key Path:of Object:change:context: .

Yuck. What makes this API so unsightly is the fact that those last two parameters are almost always 0 and NULL , respectively.

options refers to a bitmask of NSKey Value Observing Options . Pay particular attention to NSKey Value Observing Option New & NSKey Value Observing Option Old as those are the options you’ll most likely use, if any. Feel free to skim over NSKey Value Observing Option Initial & NSKey Value Observing Option Prior :

NSKeyValueObservingOptions

NSKey Value Observing Option New : Indicates that the change dictionary should provide the new attribute value, if applicable.

: Indicates that the change dictionary should provide the new attribute value, if applicable. NSKey Value Observing Option Old : Indicates that the change dictionary should contain the old attribute value, if applicable.

: Indicates that the change dictionary should contain the old attribute value, if applicable. NSKey Value Observing Option Initial : If specified, a notification should be sent to the observer immediately, before the observer registration method even returns. The change dictionary in the notification will always contain an NSKey Value Change New Key entry if NSKey Value Observing Option New is also specified but will never contain an NSKey Value Change Old Key entry. (In an initial notification the current value of the observed property may be old, but it’s new to the observer.) You can use this option instead of explicitly invoking, at the same time, code that is also invoked by the observer’s observe Value For Key Path:of Object:change:context: method. When this option is used with add Observer:for Key Path:options:context: a notification will be sent for each indexed object to which the observer is being added.

: If specified, a notification should be sent to the observer immediately, before the observer registration method even returns. The change dictionary in the notification will always contain an entry if is also specified but will never contain an entry. (In an initial notification the current value of the observed property may be old, but it’s new to the observer.) You can use this option instead of explicitly invoking, at the same time, code that is also invoked by the observer’s method. When this option is used with a notification will be sent for each indexed object to which the observer is being added. NSKey Value Observing Option Prior : Whether separate notifications should be sent to the observer before and after each change, instead of a single notification after the change. The change dictionary in a notification sent before a change always contains an NSKey Value Change Notification Is Prior Key entry whose value is @YES , but never contains an NSKey Value Change New Key entry. When this option is specified the change dictionary in a notification sent after a change contains the same entries that it would contain if this option were not specified. You can use this option when the observer’s own key-value observing-compliance requires it to invoke one of the -will Change... methods for one of its own properties, and the value of that property depends on the value of the observed object’s property. (In that situation it’s too late to easily invoke -will Change... properly in response to receiving an observe Value For Key Path:of Object:change:context: message after the change.)

These options allow an object to get the values before and after the change. In practice, this is usually not necessary, since the new value is generally available from the current value of the property.

That said, NSKey Value Observing Option Initial can be helpful for reducing the code paths when responding to KVO events. For instance, if you have a method that dynamically enables a button based on the text value of a field, passing NSKey Value Observing Option Initial will have the event fire with its initial state once the observer is added.

As for context , this parameter is a value that can be used later to differentiate between observations of different objects with the same key path. It’s a bit complicated, and will be discussed later.

Responding

Another aspect of KVO that lends to its ugliness is the fact that there is no way to specify custom selectors to handle observations, as one might be used to from the Target-Action pattern used by controls.

Instead, all changes for observers are funneled through a single method— -observe Value For Key Path:of Object:change:context: :

- ( void ) observe Value For Key Path :( NSString * ) key Path of Object :( id ) object change :( NSDictionary * ) change context :( void * ) context

Those parameters are the same as what were specified in –add Observer:for Key Path:options:context: , with the exception of change , which are populated from whichever NSKey Value Observing Options options were used.

A typical implementation of this method looks something like this:

- ( void ) observe Value For Key Path :( NSString * ) key Path of Object :( id ) object change :( NSDictionary * ) change context :( void * ) context { if ([ key Path is Equal To String : @"state" ]) { … } }

Depending on how many kinds of objects are being observed by a single class, this method may also introduce -is Kind Of Object: or -responds To Selector: in order to definitively identify the kind of event being passed. However, the safest method is to do an equality check to context —especially when dealing with subclasses whose parents observe the same keypath.

Correct Context Declarations

What makes a good context value? Here’s a suggestion:

static void * XXContext = & XXContext ;

It’s that simple: a static value that stores its own pointer. It means nothing on its own, which makes it rather perfect for <NSKey Value Observing> :

- ( void ) observe Value For Key Path :( NSString * ) key Path of Object :( id ) object change :( NSDictionary * ) change context :( void * ) context { if ( context == XXContext ) { if ([ key Path is Equal To String : NSString From Selector ( @selector ( is Finished ))]) { } } }

Better Key Paths

Passing strings as key paths is strictly worse than using properties directly, as any typo or misspelling won’t be caught by the compiler, and will cause things to not work.

A clever workaround to this is to use NSString From Selector and a @selector literal value:

NSString From Selector ( @selector ( is Finished ))

Since @selector looks through all available selectors in the target, this won’t prevent all mistakes, but it will catch most of them—including breaking changes made by Xcode automatic refactoring.

- ( void ) observe Value For Key Path :( NSString * ) key Path of Object :( id ) object change :( NSDictionary * ) change context :( void * ) context { if ([ object is Kind Of Class :[ NSOperation class ]]) { if ([ key Path is Equal To String : NSString From Selector ( @selector ( is Finished ))]) { } } else if (...) { … } }

Unsubscribing

When an observer is finished listening for changes on an object, it is expected to call –remove Observer:for Key Path:context: . This will often either be called in -observe Value For Key Path:of Object:change:context: , or -dealloc (or a similar destruction method).

Safe Unsubscribe with @try / @catch

Perhaps the most pronounced annoyance with KVO is how it gets you at the end. If you make a call to –remove Observer:for Key Path:context: when the object is not registered as an observer (whether because it was already unregistered or not registered in the first place), an exception is thrown. The kicker is that there’s no built-in way to even check if an object is registered!

Which causes one to rely on a rather unfortunate cudgel @try with an unhandled @catch :

- ( void ) observe Value For Key Path :( NSString * ) key Path of Object :( id ) object change :( NSDictionary * ) change context :( void * ) context { if ([ key Path is Equal To String : NSString From Selector ( @selector ( is Finished ))]) { if ([ object is Finished ]) { @try { [ object remove Observer : self for Key Path : NSString From Selector ( @selector ( is Finished ))]; } @catch ( NSException * __unused exception ) {} } } }

Granted, not handling a caught exception, as in this example, is waving the [UIColor white Color] flag of surrender. Therefore, one should only really use this technique when faced with intermittent crashes which cannot be remedied by normal book-keeping (whether due to race conditions or undocumented behavior from a superclass).

Automatic Property Notifications

KVO is made useful by its near-universal adoption. Because of this, much of the work necessary to get everything hooked up correctly is automatically taken care of by the compiler and runtime.

Classes can opt-out of automatic KVO by overriding +automatically Notifies Observers For Key: and returning NO .

But what about compound or derived values? Let’s say you have an object with a @dynamic , readonly address property, which reads and formats its street Address , locality , region , and postal Code ?

Well, you can implement the method key Paths For Values Affecting Address (or its less magical catch-all, +key Paths For Values Affecting Value For Key: ):

+ ( NSSet * ) key Paths For Values Affecting Address { return [ NSSet set With Objects : NSString From Selector ( @selector ( street Address )), NSString From Selector ( @selector ( locality )), NSString From Selector ( @selector ( region )), NSString From Selector ( @selector ( postal Code )), nil ]; }

So there you have it: some general observations and best practices for KVO. To an enterprising NSHipster, KVO can be a powerful substrate on top of which clever and powerful abstractions can be built. Use it wisely, and understand the rules and conventions to make the most of it in your own application.