macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

The Cocoa Controller Layer

by Michael Beam, coauthor of Cocoa in a Nutshell
04/06/2004

When we're first learning Cocoa (or Java, or Qt, or any other application framework), one of the early things we address is the Model-View-Controller design pattern. The Model-View-Controller (MVC) design pattern prescribes that a well-designed application will separate data-specific code (the model layer) from user-interface-specific code (the view layer), and that the two layers will communicate with each other by way of the controller layer.

From its inception, Cocoa has provided excellent support for model and view layer objects. Foundation classes such as NSMutableString, NSDictionary, and NSNumber provide firm footing for model classes, while AppKit's NSView and its myriad subclasses provide a solid implementation of view layer classes. When it comes to making these two domains talk to each other, however, it has always been the responsibility of the developer to write the glue code for the controller layer. And let's face it: if you've developed any application of moderate complexity, you know that writing this glue code can be downright tedious.

Apple recognized this problem and set out to provide a solution that was made available in Mac OS X 10.3. Their solution is the Controller Layer, which is comprised of a number of classes and protocols that implement a cohesive architecture for facilitating communication between an application's model and view layers.

The controller layer is the confluence of several new and existing Cocoa technologies including key-value coding, key-value observing, and key-value binding. By Apple's reckoning, the controller layer potentially allows 90 percent of Cocoa applications to replace custom-developed glue code with the components of the controller layer. In the grand tradition of Cocoa, making use of the controller layer is done almost entirely in Interface Builder, thus minimizing the amount of code you need to write. The benefit for us is clear: We can cut down or eliminate altogether the countless hours we spend implementing and maintaining the code that connects our data models to the outlet's and actions for each and every one of our user-interface controls.

What Is the Controller Layer?

Related Reading

Cocoa in a Nutshell
A Desktop Quick Reference
By Michael Beam, James Duncan Davidson

The controller layer is made up of a number of classes and protocols. The core controller classes defined in the AppKit are:

  • NSController
  • NSObjectController
  • NSArrayController
  • NSUserDefaultsController

NSController is an abstract base class that defines the general interface for all controller classes. NSObjectController is a simple concrete subclass of NSController that allows user-interface objects to be bound to a single object, while NSArrayController—itself a subclass of NSObjectController—manages a collection of objects. NSUserDefaultsController is another concrete subclass of NSController that provides an interface between the user-defaults database, and properties and values of controls in your user interface.

So what exactly do these controllers do? In short, they connect properties of model objects to user-interface elements that can display and manipulate the values of the associated properties. Consider for a moment, that our data model is a class that represents a book. Some of the properties of this model are the book title and author, which may be stored as NSString instance variables of the class Book.

Using an NSObjectController, we could associate an NSTextField to each of these properties, and the mechanisms implemented in the controller layer take care of displaying the value of the property, storing changes made in the text fields to the data model, and notifying other interested interface elements of any changes to the data. Using an NSArrayController, we can manage a collection of Book objects, and display that collection in an NSTableView, which is exactly what we will be doing today in this article. All we have to do is establish the association in Interface Builder between view, controller, and model.

Controller Layer Mechanics

Before we get into our application for the day, let's focus more on the mechanisms that form the basis of the controller layer. The mechanisms referred to here are Key-Value Coding, which a Foundation protocol, and Key-Value Observing, which is implemented in a number of new NSObject methods. The controller layer brings these two mechanisms together into Key-Value Binding, which forms the fundamental fabric of the controller layer.

Key-Value Coding

Key-Value Coding (KVC) is one of the core Cocoa technologies that has existed for some time. The goal of KVC is to provide a mechanism to indirectly access an object's property values (i.e. instance variables) by name through the use of key strings, rather than by directly invoking accessor methods. The methods used to access object properties are defined in the foundation protocol NSKeyValueCoding.

As an example of how to use KVC, consider our Book class, which might have the following simple class interface:

@interface Book : NSObject {
    NSString *_title;
    NSString *_author;
}
- (NSString *)title;
- (NSString *)author;

- (void)setTitle:(NSString *)title;
- (void)setAuthor:(NSString *)author;
@end

We see that the book class has two NSString instance variables, _title and _author, along with accessor methods for setting and getting the values of those instance variables. Ordinarily we would access these properties by their respective accessor methods. If we wanted to set the title of the book object, we would do the following:

[aBook setTitle:@"The Illiad"];

Retrieving the title of the book involves a simple invocation of title:

NSString *title = [aBook title];

Now, with KVC we use key strings to set and retrieve property values using the methods valueForKey: and setValue:forKey:, as follows:

[aBook setValue:@"The Illiad" forKey:@"title"];

NSString *title = [aBook valueForKey:@"title"];

There are a number of rules that KVC follows for identifying the accessor method or instance variable to which a given key refers. In the case of [aBook valueForKey:@"title"] KVC will first look for public accessor method named setTitle:, and if that is not found a private accessor named _setTitle:.

If neither a public or private accessor method can be found, KVC will attempt to directly change the value of an instance variable named title or _title. KVC prefers to use accessor methods to set and retrieve property values, but will access instance variables directly if no accessor methods are implemented. It is generally recommended that you always implement accessor methods to control access to your class's instance variables. For a detailed discussion of Key-Value Coding, see Apple's Introduction to Key-Value Coding.

Key-Value Observing

Key-Value Observing (KVO) is new technology in Panther on which the controller layer relies heavily. What KVO provides is a mechanism that allows objects to be notified of changes to the properties of a specific object being observed. The controller technology relies on this KVO to facilitate communication between an application's model, controller, and view layers. Thus, by using KVO a controller object will always be aware of changes made to its managed data object, and the user interface will always be updated to reflect any changes in the controller layer.

We won't delve into the details of Key-Value Observing here today, as they won't add too much to the discussion. The important thing to take away from this is that Cocoa now has in its root class, NSObject, the means of allowing objects to observe specific changes of state in other objects.

KVC + KVO = KVB

We have learned just now that Key-Value Coding permits objects to indirectly access the properties of other objects through the use of keys. We have also learned how Key-Value Observing provides a mechanism that allows objects to observe changes to properties of other specific objects. The controller layer fuses KVC and KVO to form the fundamental communication fabric of the controller layer operates, which is a design pattern called Key-Value Binding.

Key-Value Binding allows the properties of one object to be bound to the properties of another object. KVC provides the means for an object to determine and change the value of a property in another object, while KVO allows that first object to track changes to the state of the second object. The image below shows how bindings with KVC and KVO facilitate communication and synchronization in the MVC pattern:

In this figure we see how the combination of KVC and KVO creates a binding. Here, if the user were to make a change to the spreadsheet, that change would be pushed down to the data model, while KVO would notify the controller and the bar graph that a change has been made and the view needs to be updated to reflect that change.

Using bindings, we can bind the value displayed in a text field to the value of a property of a data object managed by a controller. Remember, in all of this the controller always acts as an intermediary. We never want to bind views directly to data models (nor are we able to do as much using the standard controller classes in Cocoa).

Our job in using the controller layer technology is to specify and establish these bindings between a controller and user interface objects. In Interface Builder we can view all of the available bindings for any user interface control in the object's Bindings inspector (Command-4). First, we must define the data model for our particular application so that a controller knows what model properties are available for bindings.

Pages: 1, 2, 3

Next Pagearrow