| < Previous PageNext Page > |
So far the discussion in this chapter has focused on the fundamental application architecture of Cocoa, describing how at runtime the core objects of an application work together to facilitate event handling and drawing. But now the focus shifts somewhat to take in the full picture of a running Cocoa application. Rarely does an application create all of its constituent objects "from scratch" when it launches. Many, if not most, of these objects are stored in the application bundle as archives of object graphs. These object graphs can represent the model objects that encapsulate the application's data as it existed just before the user quit the application. Or they can be encoded representations of the windows, views, and other objects that make up the application's user interface. An application loads and unarchives object archives at runtime to re-create the original objects.
An application includes not just objects and code but the other resources in its bundle, such as images and localized strings. This section also summarizes the role that instances of the NSBundle class plays in locating and loading all types of application resources, both localized and non-localized.
Object Archives
Nib Files
Loading Application Resources
Objects in a program exist in a network of relationships with other objects. An object can own particular objects or collections of objects, it can be dependent on other objects, and it can hold references to objects in the program in order to send messages to those objects. This web of interrelated objects is known as an object graph. Object graphs can become quite complex.
An archive is a means for storing an object graph. It usually takes the form of a file but can be a stream transmitted between processes. An archive preserves the identity of each object in the graph and all the relationships that object has with all other objects in the graph. It encodes the type of each object along with the object's data. When an object graph is unarchived, each decoded object is typically of the same class as the object that was originally encoded into the stream. An object's instance data is also decoded and is used to reconstitute the object. The relationships among objects in the graph are also restored. As a result, an unarchived graph should almost always duplicate the original object graph.
Note: Many Cocoa applications use archives as a persistent store for their model objects. However, the Core Data framework (a Cocoa framework) uses a more sophisticated mechanism for object persistence. See “Other Cocoa Architectures” (in this document) andCore Data Programming Guide for more information.
Archiving is an operation that requests each object in an object graph to encode itself into a stream; unarchiving does the reverse, requesting that each object decode itself. Both operations are initiated with a message sent to the root object of the graph. An object that wants to be saved to an archive must encode itself when asked to; to be restorable, it must be able to decode itself. The two types of archiving in Cocoa, sequential and keyed, reflect different styles for encoding and decoding objects. Sequential archiving requires objects to encode and decode their instance data in the same order. Keyed archiving (a more modern approach) permits each piece of stored instance data to be stored and retrieved using a key (that is, an identifying string). A class that wants to archive its instances must conform to the NSCoding protocol. An object that does sequential archiving uses the encoding and decoding methods of the NSCoder class. For keyed archiving (and unarchiving), it must use the methods of the NSKeyedArchiver and NSKeyedUnarchiver classes.
Further Reading: To learn more about archiving and unarchiving objects, see Archives and Serializations Programming Guide for Cocoa.
Nearly all Cocoa developers use the Interface Builder application to construct the user interface of their applications. (There's no requirement to use Interface Builder, but it makes the job of a developer so much easier.) Figure 6-22, which you might recall from an earlier chapter, shows a typical arrangement of Interface Builder windows. To create a user interface, you drag objects such as text views, buttons, and table views from from palettes and drop them on windows; you then position and size the objects and set their other attributes. You can also make various kinds of connections—outlets, target-action, and bindings—between these objects. Interface Builder also lets you make the initial definitions of custom model and controller classes for the purpose of making connections to and from proxy instances of those classes. And, using Interface Builder, you can specify a placeholder object assigned to a custom NSView subclass. When you finish work on a user interface in Interface Builder, you can save it as a nib file in the project as a localized resource. When the project is built, the nib file is copied to the same localization (.lproj) folder in the application bundle.
Nib files are archives whose object graphs describe an entire or partial user interface. These graphs represent the complex relationships within a user interface, including a window's view hierarchy and the various connections between objects. The descriptions of these object graphs use XML as a representation language. (You should never attempt to directly edit the XML in a nib file.)
The root object of a graph appears in the Instances pane of the nib file window (the lower-left window in Figure 6-22). In this example, the Panel instance is a root object (since it contains a view hierarchy). A nib file could have other root objects as well as NSController objects (used for bindings) and proxy instances of custom classes. In addition, each nib file window of a Cocoa application has two special kinds of instances:
File's Owner. An object that owns the nib file and manages the objects within it. The File's Owner must be external to the nib file. You use the File's Owner object as the conduit for connections between objects in the nib file and objects outside of it.
First Responder. An object representing the first responder in the responder chain (see “Responders and the Responder Chain”). In target-action connections you can specify the First Responder object as target; when a control or menu sends an action message, the application searches the responder chain (starting with the first responder) until it finds an object that can handle the message.
The standard objects on Interface Builder palettes are allocated and initialized when a user interface is built and later are archived when the objects are saved to a nib file. When the nib file is unarchived, these objects are restored. If you create a custom subclass based on the class for a standard palette object, Interface Builder encodes the superclass when it archives the object but has the custom class swapped in when the object is unarchived. In either case, the initializer for the unarchived object is not called. However, if you have a custom class of NSView (represented by the Custom View palette object), the initializer of the class is called when the Custom View object is unarchived. In any case, the application sends each custom class associated with a nib file an awakeFromNib message when all objects in a nib file have been unarchived; this message gives the class an opportunity to establish connections or perform any other set-up tasks.
Each Cocoa application has a main nib file that contains the application-specific menus and possibly one or more windows. NSApp is File's Owner of the main nib file. When an application launches, NSApp loads the main nib file, unarchiving it, and displays the menus and initial windows. Many applications have auxiliary nib files for such things as documents and panels; these nib files are loaded on demand (that is, when the user requests behavior provided by objects in the nib file).
Further Reading: You can find out more about nib files (including how to dynamically load them) by reading Resource Programming Guide.
Nib files are an application resource, as are image files, sound files, help files, and other kinds of data. Application resources can be localized—that is, they can be adapted to multiple languages and locales. For nib files, localization primarily means translating the strings that appear in a user interface, but other modifications might also be required. Text fields, buttons, and other user-interface objects might have to be resized to accommodate the new strings. The formats of dates and numbers might also have to change.
Internationalization denotes the developmental infrastructure that supports localization. When internationalizing a software product, you must put resources for a language or locale in a particular location inside a bundle's Resources directory. This location is a folder whose name identifies a language and perhaps a locale, either as a well-known language string or an abbreviation conforming to the ISO 639-1, ISO 639-2, and (for locales) ISO 3166-1 specifications. The extension of localization folders is .lproj. Users compose their list of preferred localizations in the International pane of System Preferences, and an application selects resources from the .lproj folder that corresponds to the first matching localization. Xcode offers support for internationalizing application resources; it creates the bundle structure, including the .lproj folders, and automatically populates them.
Application resources can also be nonlocalized. Nonlocalized resources go just under the application bundle's Resources directory, outside of any .lproj directory.
At runtime, an application can locate an application resource within a bundle and load it into memory. It uses an instance of the NSBundle class for this purpose. Methods of this class, when given the name and extension of a resource, return the file-system path to that resource. Application Kit categories on NSBundle allow an application to locate and load nib files, help files, and sound and image files. For example, the NSBundle class method loadNibNamed:owner: finds and loads a nib file that has the specified name and is owned by the referenced object. Loading resources dynamically this way is a programming practice that contributes to the overall efficiency of the application. An application should load resources into memory only when users request those resources.
An application can dynamically load more than file-based resources. It can programmatically load localized strings in certain contexts (for example, dialogs where the displayed message might vary). The localized string is fetched from a "strings" file (that is a file with an extension of .strings) stored in one of a bundle's .lproj directories. An application's main bundle can also contain subsidiary bundles, called loadable bundles. Loadable bundles can contain their own code (and resources), and an application, using NSBundle objects, can dynamically load this code into memory. Loadable bundles make it possible to extend application behavior flexibly through a kind of plug-in architecture. (The Automator application is an example of this architecture; it dynamically loads actions, which are loadable bundles.)
Further Reading: For more information about localization and internationalization, see Internationalization Programming Topics. To learn more about NSBundle and bundles, see Resource Programming Guide andCode Loading Programming Topics for Cocoa.
| < Previous PageNext Page > |
© 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-12-20)
|