BUY THIS BOOK
Add to Cart

Print Book $39.95


Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint this content?


Cocoa in a Nutshell
Cocoa in a Nutshell A Desktop Quick Reference

By Michael Beam, James Duncan Davidson
Price: $39.95 USD
£28.50 GBP

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Objective-C
Objective-C is a highly dynamic, message-based object-oriented language. Consisting of a small number of additions to ANSI C, Objective-C is characterized by its deferral of many decisions until runtime, supporting its key features of dynamic dispatch, dynamic typing, and dynamic loading. These features support many of the design patterns Cocoa uses, including delegation, notification, and Model-View-Controller (MVC). Because it is an extension of C, existing C code and libraries, including those based on C++, can work with Cocoa-based applications without losing any of the effort that went into their original development.
This chapter is an overview of Objective-C's most frequently used features. If you need more detail about these features or want to see the full language specification, read through Apple's document, The Objective-C Programming Language, which is installed as part of the Developer Tools in /Developer/Documentation/Cocoa/ObjectiveC.
The base unit of activity in all object-oriented languages is the object— an entity that associates data with operations that can be performed on that data. Objective-C provides a distinct data type, id, defined as a pointer to an object's data that allows you to work with objects. An object may be declared in code as follows:
id anObject;
For all object-oriented constructs of Objective-C, including method return values, id replaces the default C int as the default return data type.
The id type is completely nonrestrictive. It says very little about an object, indicating only that it is an entity in the system that can respond to messages and be queried for its behavior. This type of behavior, known as dynamic typing , allows the system to find the class to which the object belongs and resolve messages into method calls.
Objective-C also supports static typing , in which you declare a variable using a pointer to its class type instead of id, for example:
NSObject *object;
This declaration will turn on some degree of compile time checking to generate warnings when a type mismatch is made, as well as when you use methods not implemented by a class. Static typing can also clarify your intentions to other developers who have access to your source code. However, unlike other languages' use of the term, static typing in Objective-C is used only at compile time. At runtime, all objects are treated as type
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Objects
The base unit of activity in all object-oriented languages is the object— an entity that associates data with operations that can be performed on that data. Objective-C provides a distinct data type, id, defined as a pointer to an object's data that allows you to work with objects. An object may be declared in code as follows:
id anObject;
For all object-oriented constructs of Objective-C, including method return values, id replaces the default C int as the default return data type.
The id type is completely nonrestrictive. It says very little about an object, indicating only that it is an entity in the system that can respond to messages and be queried for its behavior. This type of behavior, known as dynamic typing , allows the system to find the class to which the object belongs and resolve messages into method calls.
Objective-C also supports static typing , in which you declare a variable using a pointer to its class type instead of id, for example:
NSObject *object;
This declaration will turn on some degree of compile time checking to generate warnings when a type mismatch is made, as well as when you use methods not implemented by a class. Static typing can also clarify your intentions to other developers who have access to your source code. However, unlike other languages' use of the term, static typing in Objective-C is used only at compile time. At runtime, all objects are treated as type id to preserve dynamism in the system.
There are no class-cast exceptions like those present in more strongly typed languages, such as Java. If a variable declared as a Dog turns out to be a Cat, but responds to the messages called on it at runtime, then the runtime won't complain.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Messaging
Objects in Objective-C are largely autonomous, self-contained, opaque entities within the scope of a program. They are not passive containers for state behavior, nor data and a collection of functions that can be applied to that data. The Objective-C language reinforces this concept by allowing any message— a request to perform a particular action—to be passed to any object. The object is then expected to respond at runtime with appropriate behavior. In object-oriented terminology, this is called dynamic binding .
When an object receives a message at runtime, it can do one of three things:
  • Perform the functionality requested, if it knows how.
  • Forward the message to some other object that might know how to perform the action.
  • Emit a warning (usually stopping program execution), stating that it doesn't know how to respond to the message.
A key feature here is that an object can forward messages that it doesn't know how to deal with to other objects. This feature is one of the significant differences between Objective-C and other object-oriented languages such as Java and C++.
Dynamic binding, as implemented in Objective-C, is different than the late binding provided by Java and C++. While the late binding provided by those languages does provide flexibility, it comes with strict compile-time constraints and is enforced at link time. In Objective-C, binding is performed as messages are resolved to methods and is free from constraints until that time.
Message expressions in Objective-C are enclosed in square brackets.
The expression consists of the following parts: the object to which the message is sent (the receiver), the message name, and optionally any arguments. For example, the following message can be verbalized as "send a play message to the object identified by the iPod variable":
[iPod play];
Any arguments in a message expression appear after colons in a message name. For example, to tell the iPod object to set the volume, send it the following message:
[iPod setVolume:11];
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Classes
Objects in Objective-C are defined in terms of a class. New classes of objects are specializations of a more general class. Each new class is the accumulation of the class definitions that it inherits from and can expand on that definition by adding new methods and instance variables or redefining existing methods to perform new or expanded functionality. Like Java and Smalltalk, but unlike C++, Objective-C is a single inheritance language , meaning that a class can inherit functionality only from a single class.
A class is not just a blueprint for building objects; it is itself an object in the runtime that knows how to build new objects. These new objects are instances of the class.
Every class hierarchy begins with a root class that has no superclass. While it is possible to define your own root class in Objective-C, the classes you define should inherit, directly or indirectly, from the NSObject class provided by the Foundation framework. The NSObject class defines the behavior required for an object to be used by the Cocoa framework and provides the following functionality:
  • Defines the low-level functionality needed to handle object initialization, duplication, and destruction.
  • Provides mechanisms to aid Cocoa's memory management model.
  • Defines functionality for an object to identify its class membership and provide a reasonable description of the object.
In Objective-C, classes are defined in two parts, usually separated into two different files:
  • An interface, which declares a class's methods and instance variables, and names its superclass. The interface is usually specified in a file with the .h suffix typical of C header files.
  • An implementation , which contains the code that defines the class's methods. By convention, files containing the implementation of a class have a .m suffix.

Section 1.3.2.1: The interface

To declare a class and give all the information other classes (and other programs) need to use it, an interface file needs to contain the following information:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Creating Object Instances
One of the principal functions of a class object is to serve as a factory for creating new instances. When new objects are created, memory is allocated and its instance variables are initialized. This is accomplished by using the alloc method, defined by the NSObject class, as follows:
Song song = [song alloc];
The alloc class method dynamically allocates memory, sets the isa variable to a pointer to the class's class object, sets all other variables to 0, and then returns the new object instance. This takes care of the system level tasks that need to be performed when an object is created, but doesn't allow the object to properly initialize itself. To give an opportunity for object-specific initialization, the NSObject class provides the init instance method. To fully create an instance of the Song class, use the following code:
Song song = [[song alloc] init];
The init method can be overridden in a subclass to assign defaults to instance variables and to take care of other tasks that need to be performed before an object is used.
You can call the alloc and init methods by using separate lines of code. However, since object allocation and initialization are interlinked, calling both methods with one line of code is good practice.
When you override the init method, the superclass's init method (or designated initializer, as covered in the next section) should always be called to ensure that the superclass is initialized properly. Initialization methods should also return self, the object being initialized. Example 1-6 shows an init method for the Song class.
Example 1-6. An initialization method for the Song class
- (id)init {                                     // 1
    self = [super init];                         // 2
    // ... Song-specific initialization code
    return self;                                 // 3
}
The code shown in Example 1-6 performs the following tasks:
  1. Declares the init method, which returns an object of type id. The returned object is the newly initialized object.
  2. Calls the init method of the superclass (
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Memory Management
To properly manage memory, Cocoa provides a reference counting mechanism, supported by the NSObject and NSAutoreleasePool classes. As its name suggests, reference counting maintains a count of how many references there are to an object—indicating how many other objects are interested in keeping the object around. Reference counting is not automatic; the compiler has no way to determine an object's lifetime. Therefore, the following NSObject reference counting methods must be called to indicate the level of interest in an object to the memory management system:
retain
Increments the object's reference count by 1. When you want to register interest in an object that you did not create or copy, indicate interest in it by calling this method.
release
Decrements the object's reference count by 1. This message is sent to objects created with the alloc method or sent a retain message when you are no longer interested in using them. If this causes the retain count to reach 0, the runtime deallocates the object.
autorelease
Adds the object to the current autorelease pool . This allows you to release your interest in an object without immediately causing the retain count to reach 0. When the autorelease pool is itself released, it sends the release message to every object it contains. This is most useful when you want to pass the object to another object as a return value and won't have the opportunity to release the object later by yourself.
The following set of rules will help you perform accurate reference counting and avoid either leaking memory or prematurely destroying objects:
  • Objects created by alloc or copy have a retain count of 1.
  • If you want to keep an object received from another mechanism, send it a retain message.
  • When you are done with an object created by alloc or copy, or retained by the retain message, send it a release message.
  • When you add an object to a collection, such as an array or dictionary (described in Chapter 2), the collection retains it. You are no longer responsible for the object, and you may safely release any interest in it.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Deallocating Objects
When an object is ready to be destroyed (as determined by the reference counting mechanism), the system will give the object an opportunity to clean up after itself by calling the dealloc method defined by NSObject. If the object has created or retained any other objects' reference by its instance variables, it must implement this method and perform the appropriate tasks to maintain integrity of the reference counting system.
In Example 1-8, the Song class retains the title instance variable in the setTitle: method. To properly implement memory management, you need to balance this retain with a release. Example 1-10 shows the release performed in the Song class's dealloc method.
Example 1-10. Implementing a dealloc method
- (void)dealloc {
    [title release];
    [super dealloc];
}
This provides proper balance in the reference counting mechanism.
You should never call the dealloc method yourself. Always let the memory management methods do it.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Categories
Inheritance is not the only way to add functionality to a class. With an Objective-C language construct called a category , you can add methods to an existing class, thereby extending its functionality—and the functionality of its subclasses.
A category interface declaration looks like a class interface declaration, with one exception: the category name is listed in parentheses after the class name, and the superclass is not mentioned. For example, if you wanted to add a rot13 method to the NSString class to get the rot13 version of any string, the category interface would be defined as shown in Example 1-11.
Example 1-11. Defining a category interface
#import "NSString.h"

@interface NSString (Obfuscation)
 
- (NSString *)rot13;

@end
The category's implementation looks like the implementation of a class itself. Example 1-12 shows an interface implementation.
Example 1-12. Implementation of a category
#import "Obfuscation.h"

@implementation NSString (Obfuscation)

- (NSString *)rot13 {
    NSString * rot13string;

    // Perform logic to shift each character by 13 
    return rot13string;
}

@end
Remember that a category can't declare new instance variables for a class; it can only add methods to an existing class.
A category is not a substitute for a subclass. You should not redefine methods already in a class or a class's superclass—add only new methods to the class.
Class and category interfaces define the methods that belong to a particular class. However, you might want many different classes, otherwise unrelated to one another, to perform the same set of methods. Objective-C does not support multiple inheritance, but because of the language's dynamic nature, its support for protocols (declaration of a group of methods under a name) fills the need. A protocol defines the methods that a class is expected to implement in order to function appropriately while leaving the implementation of those methods to the class.
Like classes and categories, protocols are defined in interface header (.h) files. To define a set of methods that apply to objects controlled by a media player, define the protocol as shown in Example 1-13.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Naming Conventions
Several naming conventions have become widespread within the Objective-C community. To create code that your peers can maintain more easily, try to use the following conventions:
  • Always capitalize class names.
  • Begin variable and method names with lowercase letters. If a variable or method name consists of multiple words, capitalize the first letter of the second and any following words. This practice is known as camelcase.
  • Begin accessor methods that set an instance variable value with the word "set," and make sure the instance variable name follows in camelcase.
  • Give accessor methods that return the value of an instance variable the same name as the variable. It is also acceptable—though uncommon—to prefix the variable name with the word "get" and have the instance variable name follow in camelcase.
  • Do not begin method names that you create with an underscore. By convention, Apple uses underscores to implement system level private functionality.
We've implemented these conventions throughout the book.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Foundation
The Foundation framework provides support for a variety of basic functionalities and data types, including the following:
  • Strings, numbers, and collections
  • Dates and time
  • Binary data
  • Means of working with files, including accessing data and working with bundles
  • Distributed event notification
  • Operating system interaction
  • Threading
This chapter discusses these subjects and provides several short examples that demonstrate of the most common methods of the key classes.
The Foundation framework provides many classes and protocols that extend the capabilities of the Objective-C language to represent and work with basic data types, such as strings and numbers, in an object-oriented fashion. Additionally, the Foundation framework provides application programming interfaces (APIs) for working with more complex data types, such as dates and collections.
Classes such as NSString and NSArray are immutable classes; instances of these classes cannot be altered after they are initialized. Each immutable class, however, has a mutable subclass: for example, NSString has the mutable subclass NSMutableString, and NSArray has the subclass NSMutableArray. Mutable subclasses extend their superclass's functionality to allow modification after initialization. Immutable classes are more efficient, but mutable classes are more flexible.
Two of the most basic data types in an application are strings and numbers . The Foundation framework provides object abstractions in the form of NSString and NSNumber, and an extensive API to manipulate them.

Section 2.1.2.1: Strings

Foundation's primary class used to represent and manipulate strings is NSString. Instances of NSString can be considered, at their core, an immutable array of Unicode characters, and can represent characters from the alphabets of nearly every written language, past and present. In fact, NSString is a class cluster, which shields the developer from a number of underlying implementation details that make string handling more efficient. This abstraction is generally relevant only when subclassing
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Data
The Foundation framework provides many classes and protocols that extend the capabilities of the Objective-C language to represent and work with basic data types, such as strings and numbers, in an object-oriented fashion. Additionally, the Foundation framework provides application programming interfaces (APIs) for working with more complex data types, such as dates and collections.
Classes such as NSString and NSArray are immutable classes; instances of these classes cannot be altered after they are initialized. Each immutable class, however, has a mutable subclass: for example, NSString has the mutable subclass NSMutableString, and NSArray has the subclass NSMutableArray. Mutable subclasses extend their superclass's functionality to allow modification after initialization. Immutable classes are more efficient, but mutable classes are more flexible.
Two of the most basic data types in an application are strings and numbers . The Foundation framework provides object abstractions in the form of NSString and NSNumber, and an extensive API to manipulate them.

Section 2.1.2.1: Strings

Foundation's primary class used to represent and manipulate strings is NSString. Instances of NSString can be considered, at their core, an immutable array of Unicode characters, and can represent characters from the alphabets of nearly every written language, past and present. In fact, NSString is a class cluster, which shields the developer from a number of underlying implementation details that make string handling more efficient. This abstraction is generally relevant only when subclassing NSString, so it will not be considered further here.
Objective-C provides a syntax shortcut to create strings in code that is of the form @"...". In code, this looks like:
NSString *str = @"Hello";
When interpreted by the compiler, this syntax translates into an NSString object that is initialized with the 7-bit ASCII encoded string between the quotes. This string object is created at compile-time and exists for the life of the application. While you may send
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Key-Value Coding
Key-value coding lets you access the properties of an object (such as the instance variables) indirectly by using strings referred to as keys . Although key-value coding can access instance variables directly, it first tries to use accessor methods to access a property. However, accessor methods are not necessarily mapped to instance variables, which means that an accessor may provide a property value that is computed (perhaps from instance variables).
Key-value coding is a powerful feature of Cocoa that forms the basis of many important technologies. For example, Cocoa's scripting capability is heavily based on the functionality of key-value coding.
The methods that provide an interface to key-value coding are declared in the Foundation framework's NSKeyValueCoding protocol. The principal methods are valueForKey: and takeValue:forKey:, which get and set the instance variable associated with the specified key. NSObject provides default implementations of the methods of NSKeyValueCoding. These default implementations associate keys with instance variables based on a simple set of rules. The methods that return a value, valueForKey: for instance, attempt to access the property specified by the string @"key" using the following means:
  1. A public accessor of either the form key or getKey.
  2. A private accessor method of either the form _key or _getKey.
  3. An instance variable named either key or _key.
  4. Finally, if none of these first three attempts results in anything, the method invokes handleQueryWithUnboundKey:. The default implementation raises an exception; classes may choose to provide another implementation suitable to their needs.
Methods that set the values of properties, such as takeValue:forKey:, attempt to access those properties in a similar fashion, assuming again that the key is the string @"key":
  1. A public accessor of the form setKey:.
  2. A private accessor of the form _setKey:.
  3. An instance variable named either key or _key.
  4. Finally, if none of these first three rules results in anything, the methods invoke
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Working with Files
The Foundation framework provides access to data stored in files in several ways. All of the basic data classes have methods for initializing objects from the contents of files, and for writing the data represented by the object to a file. In addition to these convenience facilities, Foundation provides two classes that provide a much higher level of interaction with files and the filesystem: NSFileManager and NSFileHandle.
The NSFileManager class is an interface that applications use to access and manipulate files and directories in the filesystem; instances of NSFileManager provide a doorway to the filesystem for application developers. Several of NSFileManager's methods call for a handler: argument. The handler is an object that should implement fileManager:willProcessPath: and fileManager:shouldProceedAfterError: methods. These callback methods allow for error handling and confidence testing with respect to the operation being performed. In Example 2-26, nil is passed to handler: for the sake of clarity.
Additionally, methods that deal with movement around in the filesystem and perform operations on files and directories typically return a BOOL value, to indicate an operation's success or failure. Finally, methods that create new files or directories usually take a dictionary with file attributes as an argument. The attributes dictionary may take values to set the file's owner, group owner, modification date, POSIX permissions; determine whether the extension is hidden; and finally, set the HFS type and creator codes. Any unspecified attribute will take on the default value. Example 2-26 shows how to work with file managers and the filesystem.
Example 2-26. Working with NSFileManager
                     // Return the default manager for the filesystem
NSFileManager *fm = [NSFileManager defaultManager];

// Change the current directory; returns YES if successful
BOOL b = [fm changeCurrentDirectoryPath:@"/usr"];

// Return the path to the current directory; returns "/usr"
NSString *p = [fm currentDirectoryPath];

// Create a new directory at the path with default attributes
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Bundles and Resource Management
A bundle is an abstraction that represents a collection of resources, such as image files, nib files, or loadable code, stored within a folder. Bundles are used pervasively in Mac OS X—applications are themselves bundles, as are preference pane and screensaver modules and application plug-ins. Although bundles are directories, the Finder often presents them to the user as a single file. For a more in-depth discussion of bundles and their usage in Mac OS X, see Inside Mac OS X: System Overview (/Developer/Documentation/Essentials/SystemOverview/SystemOverview.pdf ).
NSBundle provides an interface to bundles in the filesystem. Every Cocoa application has at least one bundle—the main bundle, accessed using the mainBundle method—that represents the application. To load other bundles, use the methods initWithPath: or bundleWithPath:. To access a bundle containing a given class, use bundleForClass:.
Using NSBundle , you can obtain the paths to resources without knowledge of a bundle's internal directory structure or what localization is used. Methods that find a resource come in two flavors: those that retrieve individual resources, whose names are on the base method name pathForResource:ofType:, and those that return all resources of a type, which are based on the method name pathsForResourceOfType:. The paths returned by these methods are absolute paths in the filesystem. For example, consider the method pathForResource:ofType:. Given the name of the resource (resource file name sans extension) and, optionally, the type (the extension may pass nil or @"" here), this method will return the full path to the specified resource in the main resources directory, which is at BundleName/Contents/Resources. If the resource is not found there, then any .lproj folders are searched in order according to the user's Language setting in Preferences.
If you want to specify a directory to search in for the resource (as it may not be contained in the Resources directory), use the method pathForResource:ofType:inDirectory:. If the resource is not present in the specified directory, the method returns
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Archiving Objects
Archiving an object (or a collection of interconnected objects, known as a graph) into a NSData representation is often useful or necessary. Objects that are archived into an NSData object can be transported over network connections or interprocess communication channels and saved to the filesystem. Later, the original graph of objects can be reconstituted from the archive data.
Foundation provides five classes to support the creation and extraction of archives, all subclasses of NSCoder:
  • NSArchiver
  • NSUnarchiver
  • NSKeyedArchiver
  • NSKeyedUnarchiver
  • NSPortCoder
NSCoder declares the common interface for encoding and decoding objects and other Objective-C data types. For example, the encodeObject method encodes an object into an archive, and methods such as encodeInt: and encodeRect: support encoding C data types such as integers and common Cocoa data structures.
NSCoder does not implement these methods; it is an abstract class. Rather, subclasses implement the appropriate methods for their particular purpose. NSArchiver and NSUnarchiver provide a straightforward way of encoding and decoding objects and scalars, but they have limitations. The biggest limitation is that objects in an archive can be decoded only in the same order in which they were encoded. Because of this constraint, changing an encoding system is difficult once it has been established publicly.
The NSKeyedArchiver and NSKeyedUnarchiver classes solve this problem by associating keys with each object and scalar encoded in an archive. Decoders can use these keys to access an archive's contents in a convenient order that isn't constrained by a design decision made in a previous version of the application.
Keyed archiving is not available in versions prior to Mac OS X 10.2. If your application supports Mac OS X 10.1, then check whether the coder passed in
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
User Defaults
User defaults is another term for user application preferences. Mac OS X has a well-designed user defaults system that is accessed in Cocoa through the Foundation class NSUserDefaults . Working with NSUserDefaults is similar to working with an NSDictionary. Default values are stored in the database by keys that the application developer defines in the application. The defaults database is actually a collection of property list files; every application has its own property list file where defaults are stored. You can view these files in ~/Library/Preferences.
Defaults are organized into domains , which are groupings of default values that have varying degrees of visibility to applications. A domain is either persistent or volatile . Defaults in a persistent domain are stored in the defaults database, while defaults in a volatile domain are applicable only during the lifetime of the NSUserDefaults object that contains those values. NSUserDefaults has five standard domains:
NSArgumentDomain
Set values for defaults in the argument domain by passing key-value pairs to the application as arguments on the command line, (e.g., % MyApp -KeyName Value). The argument domain is volatile, so arguments affect the application only during the application session for which they were specified.
Application
Application-specific defaults are stored here and kept persistently in the user's defaults database.
NSGlobalDomain
Defaults stored in the global domain are applicable to all applications run by the user. This persistent domain is stored in the defaults database.
Languages
The languages domain stores defaults that pertain to language choice and localization.
NSRegistrationDomain
The registration domain is the lowest-level domain containing application-provided defaults (or "factory settings") used when a default value is otherwise unspecified in a higher domain.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Notifications
Notifications provide a mechanism for distributing information about events within an application. Notifications provide an alternative to messaging as a means for communicating between objects. Messaging requires that the sender of the message know who the receiver is when the message is dispatched. Effectively, the notifications system decouples the message sender from the message receiver. With notifications, a broadcast paradigm is implemented in which objects post notifications to a notification center , which then sends messages to objects (known as observers) which have registered their interest in the type of event, or the originating object.
The notification center is an instance of the NSNotificationCenter class; notifications are instances of the NSNotification class. Every notification object has a name identifying the notification type, an object associated with the notification that provides context for the notification, and an optional userInfo dictionary with which posters may pass additional information. When an observer registers with the notification center, it specifies a method to be invoked in response to the posting of a notification. Upon receiving a notification, the notification center identifies the observers of the specific named notification and invokes a predetermined method in each observer.
To obtain an instance of NSNotificationCenter, use the class method defaultCenter . This returns an application's default notification center. To register an object with the notification center, invoke the NSNotificationCenter method addObserver:selector:name:object:. The first argument in this method is the object that is added as an observer; this object is usually self. The selector: argument provides a method selector for the method that needs to be invoked in response to the notification. The parameter name: is the name of the notification, while the final argument lets you specify the object whose notifications you want to be notified of.
Observers can be flexible when specifying the granularity of which notifications they would like to respond to in the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Operating System Interaction
The Foundation framework provides many services through a small set of classes that allow developers to interact with the underlying operating system in several ways. NSTask is an object-oriented interface to configure and launch a process as a subprocess of the current application. NSProcessInfo lets an application discover information about its current process.
Example 2-35 shows some of the information you can obtain by using NSProcessInfo.
Example 2-35. Using NSProcessInfo
                     // Get the shared process info object for the current process
NSProcessInfo *proc = [NSProcessInfo processInfo];

// The name and PID of the process
NSString *name = [proc processName];
int pid = [proc processIdentifier];

// The arguments launched with the process
NSArray *args = [proc arguments];

// A dictionary of the environment variables for the process
NSDictionary *env = [proc environment];

// The host name for the host running the process
NSString *host = [proc hostName];

// The operating system version string
                     // Note: this string is NOT appropriate for parsing
NSString *ver = [proc operatingSystemVersionString];
NSTask is a class that lets a program configure, launch, and communicate with another program as a subprocess. This is something you'll see every time you execute an application you work on from within Project Builder.
An NSTask is launched in a fully configurable execution environment where environment variables, command line options, and other key points of a task may be set. By default, if no environment configuration is provided for a new task, the task inherits the environment of the parent process that launched it.
Setting up and running a task is straightforward. For example, you could run ls on the root directory, as shown in Example 2-36.
Example 2-36. The quick and dirty NSTask
NSArray *args = [NSArray arrayWithObject:@"/"];
NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/ls
                                        arguments:args];
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Threaded Programming
Cocoa uses the NSThread class to provide multiple threads of execution in applications. Threads are useful if you want to perform a computationally intensive or time-consuming procedure in the background while allowing the main thread of execution to continue normally. When executing code in the main thread of the application, the flow of execution must stop while that code completes execution. If code takes a long time to execute, the user will notice that the application user interface controls freeze and do not respond to any actions.
To correct this problem, you could isolate the time-consuming code into its own method, and then execute that method in its own thread. This is how NSThread works—using the detachNewThreadSelector:toTarget:withObject: method, as shown in Example 2-38.
Example 2-38. Multithreading an application
                  // This method is invoked in response to some user action
- (void)someActionMethod:(id)sender
{
    [NSThread detachNewThreadSelector:@selector(longCode)
                             toTarget:self withObject:nil];
}

- (void)longCode
{
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    BOOL keepGoing = YES;

    while ( keepGoing) {
        // Do something here that will eventually stop by
                   // setting keepGoing to NO
    }

    [pool release];
}
As illustrated in Example 2-38, threads exit after the natural completion of a method. If the threaded method is to integrate properly with Cocoa objects, then that method is responsible for setting up and destroying its own autorelease pool.
You can also cause a thread to exit before the natural completion of a method's execution by invoking the exit method. In fact, this is the method NSThread uses to cause a thread to exit normally at the end of a method. For example, you could insert a conditional into your threaded processing loop, as shown in Example 2-39.
Example 2-39. Using NSThread's exit method
- (void)longCode
{
  NSAutoreleasePool *pool;
  pool = [[NSAutoreleasePool alloc] init];
  BOOL exitEarly = NO;

  while ( YES ) {
    
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: The Application Kit
The bulk of the Application Kit (or AppKit) is comprised of classes for user interface components, from windows and widgets to colors and fonts. From the perspective of an application, the central class is NSApplication , which manages much of the basic application architecture, from loading user interface definitions to setting up the run loop to handle events, which relieves you of much of the nitty-gritty often associated with simply making an application tick. The AppKit also provides many architecture-related classes for document management, printing, and interaction with the workspace.
Besides leveraging the design patterns and methodologies used with the Foundation framework, the Application Kit relies heavily on several others. Two that merit special attention are Model-View-Controller and Target/Action.
The Model-View-Controller (MVC) pattern is the driving design pattern in the Application Kit. The premise of this pattern is that code may be split up into logically distinct units that each perform a specific role:
  • The model is an object that encapsulates data and provides logic that manipulates that data.
  • The view is a separate object that only knows how to display data.
  • The controller is an arbiter between the model and the view. The controller's job is to take data from the model and pass it to the view where it can be displayed. If the view is interactive—able to accept user input—then the controller will interpret those actions and instruct the data model to do something in response.
Many views in the MVC pattern may subscribe to the controller, giving the application flexibility to display data in different contexts and formats without heavy modification of the modeling and control logic. This idea is illustrated in Figure 3-1, which shows two views of the same data: a table view of the data and a chart view.
Figure 3-1: MVC used to display two different views of the same data
The Application Kit uses the target/action pattern to decouple UI widgets (controls—typically subclasses of
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
AppKit Design Patterns
Besides leveraging the design patterns and methodologies used with the Foundation framework, the Application Kit relies heavily on several others. Two that merit special attention are Model-View-Controller and Target/Action.
The Model-View-Controller (MVC) pattern is the driving design pattern in the Application Kit. The premise of this pattern is that code may be split up into logically distinct units that each perform a specific role:
  • The model is an object that encapsulates data and provides logic that manipulates that data.
  • The view is a separate object that only knows how to display data.
  • The controller is an arbiter between the model and the view. The controller's job is to take data from the model and pass it to the view where it can be displayed. If the view is interactive—able to accept user input—then the controller will interpret those actions and instruct the data model to do something in response.
Many views in the MVC pattern may subscribe to the controller, giving the application flexibility to display data in different contexts and formats without heavy modification of the modeling and control logic. This idea is illustrated in Figure 3-1, which shows two views of the same data: a table view of the data and a chart view.
Figure 3-1: MVC used to display two different views of the same data
The Application Kit uses the target/action pattern to decouple UI widgets (controls—typically subclasses of NSControl) from the code that is executed when the user activates the control. In this pattern, a control keeps a reference to a target object that receives an action message. The action message is stored as a selector in an instance variable of the control object. When the user activates the control, the action message is sent to the target. Action messages and targets are generally established in Interface Builder; however, you can change a control's behavior by using the setAction: and setTarget: methods of NSControl. The current target and action of a control may be ascertained programmatically by using the methods
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Nibs
One of the crown jewels of the Cocoa development environment is Interface Builder, a tool for building graphical user interfaces.
Interface Builder creates and stores user interfaces in nib files. Perhaps one of the most important aspects of Interface Builder is that it does not generate source code. Nibs are bundles that contain information and archived objects (in this context often referred to as "freeze-dried") that constitute the elements of the user interface (such as windows, widgets and menus, as well as non-UI objects) as they were arranged and configured with Interface Builder. When a nib file is loaded at run time, the elements are reconstituted exactly as they were in Interface Builder.
Every application has a MainMenu.nib file that contains the application's main menu structure. For simple applications, this nib might also contain the application's main window. More advanced applications generally have a number of nib files, each of which defines part of the user interface.
From a design perspective, creating a nib for each window (or logical group of windows) facilitates reuse. From a performance perspective, loading nibs into memory takes time, so it is best to keep them small. Thus, when the application launches, it needs to load only the nib containing the first window the user sees, rather than every window in the application, many of which the user may never open (such as the About Box).
Nib files contain definitions for objects that can send messages to other objects in the nib file and be assigned to instance variables of other objects. An object instance variable in Interface Builder is known as an outlet . Outlets can be connected to other objects within the nib, and the source code for the class containing the outlet can reference those other nib objects through the outlet instance variable. Objects in nibs also have actions , which are methods that other objects may invoke in response to some event, such as a user clicking a button.
When designing a class in Interface Builder, you can create skeleton source files containing instance variables and stubs for action method definitions. In the header file of a class defined in Interface Builder, outlet instance variables have the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Application Architecture
The Application Kit's basic architecture is primarily implemented in three classes: NSApplication , NSWindow , and NSView . Figure 3-3 shows the class hierarchy for these classes. Individually, these three classes provide the means for an application to interface with the operating system (and ultimately, the user) via connections to Quartz, the window server, and underlying Unix libraries through Core Foundation. Taken as a whole, these classes form the backbone of the Application Kit's event-handling infrastructure.
Figure 3-3: The hierarchical relationship between NSApplication, NSWindow, and NSView; these three classes all have a common parent in NSResponder
Fundamental to every Cocoa application is a singleton instance of NSApplication (accessible by using the class method sharedApplication or the global variable, NSApp). NSApplication provides a link to the window server and other essential operating system services. One of its most important responsibilities is management of the application's run loop and event handling. Run loops have the job of managing input from sources such as the mouse and keyboard (through the window server), ports, and timers. As the owner of the application's main run loop, NSApplication is the first stop for event processing in an application. Through a direct connection to the window server, NSApplication accepts events, packages them as Cocoa objects (instances of NSEvent), and dispatches them to the appropriate responder. NSApplication is also responsible for managing autorelease pools.
NSApplication is also concerned with other details, such as managing the main menu of an application, managing an application's Dock menu and icon, opening windows and sheets in modal run loops, hiding and unhiding the application, and application activation and deactivation. NSApplication also enables an application to connect to Mac OS X system services found in its Services menu.

Section 3.3.1.1: NSApplicationMain

Every application begins with the same function, named
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Controls
Content preview·Buy PDF of