< Previous PageNext Page > Hide TOC

The Roles of Key Objects in Document-Based Applications

Instances of the three major classes in the document architecture play distinct but cooperating roles, as described in this article.

Contents:

The Role of NSDocumentController
The Role of NSDocument
The Role of NSWindowController
Typical Usage Patterns
Documents and Scripting


The Role of NSDocumentController

The primary job of an application’s NSDocumentController object is to create and open documents, and to track and manage those documents. When a user chooses New from the File menu, an NSDocumentController object gets the appropriate NSDocument subclass from the CFBundleDocumentTypes property in the application’s information property list, allocates an instance of this class, and initializes this instance by invoking the NSDocument method initWithType:error:. When the user chooses Open from the File menu, the NSDocumentController object displays the Open panel, gets the user’s selection, finds the NSDocument subclass for the file (based on its document type information), allocates an instance of this class, and initializes the object and loads document data by invoking the NSDocument method initWithContentsOfURL:ofType:error:. In both cases, the NSDocumentController object adds a reference to the document object to an internal list to facilitate the management of its documents. It has a notion of the current document as the document whose window is currently main. (The foremost application window that is the focus of the user’s attention is called the main window.)

The NSDocumentController object manages the Open Recent menu for the application, maintaining a list of the URLs of documents the application has recently handled. It notes recent documents during the opening, saving, reverting, and closing of documents.

NSDocumentController is hard-wired to respond appropriately to certain application events, such as when the application starts up, when it terminates, when the system is shutting down, and when documents are opened or printed from the Finder. If you wish, you can make a custom object the application delegate and implement the delegate methods invoked as a result of the same events, and these methods will be invoked instead. However, the default NSDocumentController object is an adequate application controller for most situations, and typically you should not need to subclass it. If you require additional behavior, such as displaying About panels and handling application preferences, you can have a custom controller object perform these duties rather than a subclass of NSDocumentController. Nonetheless, it is possible to subclass NSDocumentController if necessary, as described in “Creating a Subclass of NSDocumentController.”

The Role of NSDocument

The primary job of an NSDocument object is to represent, manipulate, store, and load the persistent data associated with a document. As such it is a model-controller. Based on the document types it claims to understand (as specified in the CFBundleDocumentTypes property of the application’s information property list), a document must be prepared to:

With the assistance of its window controllers, an NSDocument object manages the display and capture of the data in its windows. By some special hard-wiring of the Application Kit, the NSDocument object associated with the key window is the recipient of first-responder action messages when users save, print, revert, and close documents. In response to the appropriate action, it knows how to run and manage the Save panel and the Page Layout panel.

A fully implemented NSDocument object knows how to track its edited status, print document data, and perform undo and redo operations. Although these behaviors aren’t completely provided by default, the NSDocument object does assist the developer in implementing each. For edited-status tracking, the NSDocument object provides an API for updating a change counter. For undo/redo operations, the NSDocument object by default lazily creates an NSUndoManager instance when one is requested, responds appropriately to Undo and Redo menu commands, and updates the change counter when undo and redo operations are performed. For printing, the NSDocument object facilitates the display of the Page Layout panel and the subsequent modification of the NSPrintInfo object used in printing.

The Role of NSWindowController

An NSWindowController object manages one window associated with a document, which is usually stored in a nib file. As such it is a view-controller. If a document has multiple windows, each window has its own window controller. For example, a document might have a main data-entry window and a window listing records for selection; each window would have its own NSWindowController object. When requested by its owning NSDocument object, an NSWindowController object loads the nib file containing a window and displays it. It also assumes responsibility for closing windows properly (after ensuring that the data they display is saved).

The NSWindowController class offers additional behavior to document-based applications, such as cascading document windows in relation to each other, so they don’t completely obstruct one another.

Subclasses of NSWindowController are optional. Applications can often use the default instance. Subclasses can augment NSWindowController to perform different nib-loading and setup tasks or to customize the titles of windows.

Typical Usage Patterns

This section describes three ways to use the document architecture, starting with the simplest and proceeding to the most complex.

The simplest way to use the document architecture is appropriate for documents that have only one window and are simple enough that there isn’t much benefit in splitting the controller layer into a model-controller and a view-controller. In this case, you need only to create a subclass of NSDocument. The NSDocument subclass provides storage for the model and the ability to load and save document data. It also has any outlets and actions required for the user interface. It overrides windowNibName to return the nib file name used for documents of this type. The NSDocument object automatically creates an NSWindowController object to manage that nib file, but the NSDocument object serves as the nib file's owner.

If your document has only one window, but it is complex enough that you’d like to split up some of the logic in the controller layer, you can subclass NSWindowController as well as NSDocument. In this case, any outlets and actions and any other behavior that is specific to the management of the user interface goes into the NSWindowController subclass. Your NSDocument subclass must override makeWindowControllers instead of windowNibName. The makeWindowControllers method should create an instance of your NSWindowController subclass and add it to the list of managed window controllers with addWindowController:. The NSWindowController object should be the file’s owner for the nib file because this creates better separation between the view-related logic and the model-related logic. This approach is recommended for all but the simplest cases.

If your document requires or allows multiple windows for a single document, you should subclass NSWindowController as well as NSDocument. In your NSDocument subclass you override makeWindowControllers just as in the second procedure described above. However, in this case you might create more than one instance of NSWindowController, possibly from different subclasses of NSWindowController. Some applications need several different windows to represent one document. Therefore you probably need several different subclasses of NSWindowController and you must create one of each in makeWindowControllers. Some applications need only one window for a document but want to allow the user to create several copies of the window for a single document (sometimes this is called a multiple-view document) so that the user can have each window scrolled to a different position or displayed in different ways. In this case, your makeWindowControllers override may create only one NSWindowController object, but there will be a menu command or similar control that allows the user to create others.

Documents and Scripting

Scripting support is mostly automatic for applications based on the document architecture, for several reasons. First, NSDocument and the other classes in the document architecture directly implement the standard document scripting class (as expected by AppleScript) and automatically support many of the scripting commands that apply to documents. Second, because the document architecture is intended to work with application designs that use model-view-controller (MVC) separation, and because scripting support depends on many of the same design points, applications that use the document architecture are already in better shape to support scripting than other applications that are not designed that way. Finally, the document plays an important role in the scripting API of most applications; the NSDocument class fills that role and provides a good starting point for allowing scripted access to the model layer of your application.

If an application is not based on the document architecture, making it scriptable requires you to duplicate work you would otherwise get for free. The TextEdit example application shows how to make a document-based application that is not based on NSDocument scriptable. See the Sketch example project for an example of how to implement a scriptable NSDocument-based application.



< Previous PageNext Page > Hide TOC


© 2001, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-01-12)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.