By Michael Beam, James Duncan Davidson
Price: $39.95 USD
£28.50 GBP
Cover | Table of Contents | Colophon
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;
id replaces the default C
int as the default return data type.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.id, for
example:NSObject *object;
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;
id replaces the default C
int as the default return data type.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.id, for
example:NSObject *object;
id to
preserve dynamism in the system.Dog turns out to be a
Cat, but responds to the messages called on it at
runtime, then the runtime won't complain.play message to the
object identified by the iPod
variable":[iPod play];
iPod object to set the
volume, send it the following message:[iPod setVolume:11];
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:alloc
method, defined by the
NSObject class, as follows:Song song = [song alloc];
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];
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.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.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.- (id)init { // 1
self = [super init]; // 2
// ... Song-specific initialization code
return self; // 3
}init method, which returns an object
of type id. The returned object is the newly
initialized object.init method of the superclass
(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
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
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
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.alloc or
copy have a retain count of 1.retain message.alloc
or copy, or retained by the
retain message, send it a
release message.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.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.- (void)dealloc {
[title release];
[super dealloc];
}dealloc method yourself.
Always let the memory management methods do it.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.#import "NSString.h" @interface NSString (Obfuscation) - (NSString *)rot13; @end
#import "Obfuscation.h"
@implementation NSString (Obfuscation)
- (NSString *)rot13 {
NSString * rot13string;
// Perform logic to shift each character by 13
return rot13string;
}
@endNSString 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.NSString and NSNumber, and an
extensive API to manipulate them.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 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.NSString and NSNumber, and an
extensive API to manipulate them.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.@"...". In code, this looks like:NSString *str = @"Hello";
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
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:key or
getKey._key
or _getKey.key or
_key.handleQueryWithUnboundKey:. The
default implementation raises an exception; classes may choose to
provide another implementation suitable to their needs.takeValue:forKey:, attempt to access those
properties in a similar fashion, assuming again that the key is the
string @"key":setKey:._setKey:.key or
_key.NSFileManager and NSFileHandle.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.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.
// 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 attributesNSBundle
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:.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.
pathForResource:ofType:inDirectory:. If the
resource is not present in the specified directory, the method
returns 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.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.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.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.NSUserDefaults object
that contains those values. NSUserDefaults has
five standard domains:NSArgumentDomain
NSGlobalDomain
NSRegistrationDomain
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.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.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.
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.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.NSArray *args = [NSArray arrayWithObject:@"/"];
NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/ls
arguments:args];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.NSThread works—using the
detachNewThreadSelector:toTarget:withObject:
method, as shown in Example 2-38.
// 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];
}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.- (void)longCode
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
BOOL exitEarly = NO;
while ( YES ) {
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.
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 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.
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.