From the blog

KVO and Data Binding in iOS Made Simple

It is said that

All problems in computer science can be solved by another level of indirection.
-David Wheeler

The same is often said about levels of abstraction. But with abstraction comes the problem of how the different layers interact with each other. They are inherently linked, but how do changes at one layer affect the layer above it?

In iOS we are familiar with three ways of subscribing to event notifications of another object:

  • Delegation
  • Notification Center and
  • Key-Value Observing (KVO)

Of these three patterns, KVO is the most arcane, and certainly the most misunderstood. In this post we’ll take a look at the basics of KVO and expand upon it to see how it can be leveraged in a typical application.

Wait, what is KVO?

Key-Value Observation is a mechanism in Cocoa to receive callbacks when an object changes state. An object first registers to observe a property of another object. Then, whenever that property changes value, the observer is automatically notified. No code is required in the observed object, so how does KVO actually work? It might seem like magic, but the implementation is, in fact, just a clever use of the Objective-C runtime, as Mike Ash explains in this enlightening article.

When to use KVO

KVO is limited to observing key paths of objects, so it should be used in conjunction with other event notification patterns listed above where necessary. Although not every problem requires it, there are times when KVO has distinct advantages over the other patterns.

Deferring observation logic

The most common situation is when you don’t have access to the observed object’s source code. In this case, both delegation and Notification Center are completely out of the question, since each requires the object to publish change events.

Even if you can edit the class file, KVO helps to decouple model objects from observation logic. Unless you can define a strict contract (a la delegation) for all the ways an object can change, it’s best to leave the decision of which state changes are relevant up to an external observer.

Keeping the architecture coherent

One of the most important things in an application is coherent state. This can also be one of the most difficult things to manage accross all the layers of architecture (model, view, controller, or even view model). KVO is great for keeping these layers in sync where chained delegates would be impractical at best. For example:

  • You might define a userIsAuthenticated property on your view model that you want to automatically match a similar property on your user model object.
  • You have a formComplete property that depends on several other model properties.
    In these cases and others KVO can serve as the glue between architectural layers. This process is often referred to as data binding.

Writing event-driven code

KVO encourages a design where objects react automatically to changes. Liberal use of KVO in an application can lead to an environment where the UI reacts and updates immediately as needed when underlying model objects change. Such an environment helps remove programmer error by establishing strong relationships that don’t require manual update notifications. Instead of needing to keep track of which objects need to be notified when properties change, let KVO handle it for you. The fewer things developers have to remember and maintain, the better.

So you want to observe key-values

To register an observer for a property of another object, use:

Parameters of note areoptions and context. options is a bitmask of NSKeyValueObservingOptions indicating which information you’d like to receive in the callback when the key path changes value. context can be literally any pointer, and is used only by your application to decide how to handle a callback. The context allows you to distinguish this new observer from other registered observers on the same property.

Observers must then implement the instance method:

The observer has access to the object and key path that changed, as well as a dictionary containing information about the change. The contents of this dictionary vary depending on the options used to register the observer. Note that this dictionary may also contain NSNull values, so don’t assume to know object types. The context parameter will match the context used when registering the observer.

The most important thing to remember about standard KVO is that observers must be removed before an object is deallocated via:

Failure to do so will result in a warning in iOS7, and a crash as of iOS8. Altogether, a typical KVO setup looks like:

This is a cursory overview of standard Key-Value Observation, but this NSHipster post provides some more details about this cumbersome API.

Drawbacks of standard KVO

Aside from the ugly API, standard KVO has some inherent drawbacks:

  • Have to implement an instance method.
    It is extremely unfortunate that observers must implement an instance method in order to receive change callbacks. The primary limitation is that only classes you write can subscribe to change events and react accordingly. It also means that you have to take great care not to interfere with the superclass implementation. You can do this using the context parameter, as in the example above, but then each observer has to manage its own KVO context.
  • Difficult to manage observation of multiple key paths.
    In this example only three key paths are observed, but you can imagine the branching involved in observation methods when many different objects and key paths are observed by the same object. The standard KVO API is impractical and essentially unusable in this case.
  • Observers have to be removed before deallocation.
    As long as you remember to remove observers before an observed object is deallocated, things are good. Or are they? This limitation means that objects can realistically only observe objects to which they have a strong reference, lest the observed object be deallocated before the observer and cause a crash. This may be acceptable in most cases, but many strong references to your model objects from various areas of your application can make memory management a little tricky.

The concept of KVO is so great and powerful, but its lacking API unfortunately makes it a nightmare to work with.

Data Binding Using KVO

We’ve recently been experimenting with the increasingly popular Model-View-ViewModel (MVVM) architecture here at Raizlabs, and have found that data binding is an absolute necessity for the architecture to be successful. It is the glue that connects your views, models, view models, and keeps them in sync.

Ideally, the view is bound to the view model, which is in turn bound to the model. Changes from the model then percolate upwards to be immediately reflected in the UI. Cocoa for OS X has a built-in bindings system built on KVO, but iOS has no equivalent. Third party data binding frameworks like ReactiveCocoa do exist for iOS, but use of ReactiveCocoa tends to require a significant paradigm shift. We wanted a lightweight implementation with a more familiar API.

The result was RZDataBinding, a category on NSObject that provides a simple yet powerful data binding mechanism. It’s built on standard KVO, but abstracts away from the standard API to mitigate the shortcomings listed above. RZDataBinding uses the familiar target/action model for registering callbacks for when a key path changes. There are also methods to bind the properties of two objects together, so that when one changes the other is updated automatically. Property values are bound to each other through a function, where the default is the identity function.

Using RZDataBinding, the standard KVO example above reduces to:

That’s it. The reduction in code greatly increases clarity and maintainability, but there are also several other advantages.

Callback targets don’t need to implement any methods or otherwise know about the binding, so any object can be bound to any other object.

There is also no need to worry about how the superclass has set up its bindings, since the same target can register multiple actions for the same key path.

Finally, there is no need to cleanup observers when using RZDataBinding. This means that you may bind to weakly owned objects, and there is no chance of causing a crash by forgetting to remove an observer in dealloc.

In contrast with standard KVO, RZDataBinding is simple and clear to work with. It also provides more safety, and should therefore be preferred whenever possible. The code is available here under the MIT license.

Where to go from here

If you’re searching for a lightweight data binding option for iOS, then RZDataBinding provides a solution. If you’re just looking for a way to reduce complexity, particularly complexity caused by managing state, then consider an event-driven approach. Using the right event notification pattern, whether it’s delegation, Notification Center, or KVO, greatly increases the maintainability of your code. KVO is painful in its raw form, but tools like RZDataBinding can leverage it to solve otherwise impossible problems.


Interested in joining the Raizlabs team making great software? We’re hiring developers in Boston and SF.


 

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *