3. Objective-C Boot Camp

Most iPhone development centers on Objective-C. It is the standard programming language for both the iPhone and for Mac OS X. It offers a powerful object-oriented language that lets you build applications that leverage Apple’s Cocoa and Cocoa Touch frameworks. In this chapter, you learn basic Objective-C skills that help you get started with iPhone programming. You learn about interfaces, methods, properties, memory management, and more. To round things out, this chapter takes you beyond Objective-C into Cocoa to show you the core classes you’ll use in day-to-day programming and offers you concrete examples of how these classes work.

The Objective-C Programming Language

Objective-C is a strict superset of ANSI C. C is a compiled, procedural programming language developed in the early 1970s at AT&T. Objective-C, which was developed by Brad J. Cox, adds object-oriented features to C, blending C language constructs with concepts that originated in Smalltalk-80. Smalltalk is one of the earliest and best-known object-oriented languages, which was developed at Xerox PARC. Cox layered Smalltalk’s object and message passing system on top of standard C to create his new language. This allowed programmers to continue using familiar C-language development while accessing object-based features from within that language. In the late 1980s, Objective-C was adopted as the primary development language for the NeXTStep operating system by Steve Jobs’s startup computer company NeXT. NeXTStep became both the spiritual and literal ancestor of OS X. The current version of Objective-C is 2.0, which was released in October 2007 along with OS X Leopard.

Object-oriented programming brings features to the table that are missing in standard C. Objects refer to data structures that are associated with a preset list of function calls. Every object in Objective-C has instance variables, which are the fields of the data structure, and methods, which are the function calls the object can execute. Object-oriented code uses these objects and methods to introduce programming abstractions that increase code readability and reliability.

Object-oriented programming lets you build reusable code units that can be decoupled from the normal flow of procedural development. Instead of relying on process flow, object-oriented programs are developed around smart data structures provided by objects and their methods. Cocoa Touch on the iPhone and Cocoa on Mac OS X offer a massive library of these smart objects. Objective-C unlocks that library and lets you build on Apple’s toolbox to create effective, powerful applications with a minimum of effort and code.

Note

The iPhone’s Cocoa Touch class names that start with NS, such as NSString and NSArray, hearken back to NeXT. NS stands for NeXTStep, the operating system that ran on NeXT computers.

Classes and Objects

Objects are the heart of object-oriented programming. You define objects by building classes, which act as object creation templates. In Objective-C, a class definition specifies how to build new objects that belong to the class. So to create a “widget” object, you define the Widget class and then use that class to create new objects on demand.

Each class lists its instance variables and methods in a public header file using the standard C .h convention. For example, you might define a Car object like the one shown in Listing 3-1. The Car.h header file shown here contains the interface that declares how a Car object is structured. Note that all classes in Objective-C should be capitalized.

Listing 3-1 Declaring the Car Interface (Car.h)

image

In Objective-C, the @ symbol is used to indicate certain keywords. The two items shown here (@interface and @end) delineate the start and end of the class interface definition. This class definition describes an object with three instance variables: year, make, and model. These three items are declared between the braces at the start of the interface.

The year instance variable is declared as an integer (using int). Make and model are strings, specifically instances of NSString. Objective-C uses this object-based class for the most part rather than the byte-based C strings defined with char *. As you see throughout this book, NSString offers far more power than C strings. With this class, you can find out a string’s length, search for and replace substrings, reverse strings, retrieve file extensions, and more. These features are all built into the base Cocoa Touch object library.

This class definition also declares three public methods. The first is called setMake:andModel:andYear:. This entire three-part declaration, including the colons, is the name of that single method. That’s because Objective-C places parameters inside the method name. In C, you’d use a function like setProperties(char *c1, char *c2, int i). Objective-C’s approach, although heftier than the C approach, provides much more clarity and self-documentation. You don’t have to guess what c1, c2, and i mean because their use is declared directly within the name:

[myCar setMake:c1 andModel:c2 andYear:i];

The three methods are typed as void, void, and int. As in C, these refer to the type of data returned by the method. The first two do not return data, the third returns an integer. In C, the equivalent function declaration to the second and third method would be void printCarInfo() and int year();.

Using Objective-C’s method-name-interspersed-with-arguments approach can feel odd to new programmers but quickly becomes a much-loved feature. There’s no need to guess which argument to pass when the method name itself tells you what items go where. In Objective-C, method names are also interchangeably called selectors. You see this a lot in iPhone programming, especially when you use calls to performSelector:, which lets you send messages to objects at runtime.

Notice that this header file uses #import to load headers rather than #include. Importing headers in Objective-C automatically skips files that have already been added. This lets you add duplicate #import directives to your various source files without any penalties.

Note

The code for this example, and all the examples in this chapter, is found in the sample code for this book. See the Preface for details about downloading the book sample code from the Internet.

Creating Objects

To create an object, you tell Objective-C to allocate the memory needed for the object and return a pointer to that object. Because Objective-C is an object-oriented language, its syntax looks a little different from regular C. Instead of just calling functions, you ask an object to do something. This takes the form of two elements within square brackets, the object receiving the message followed by the message itself, [object message].

Here, the source code sends the message alloc to the Car class, and then sends the message init to the newly allocated Car object. This nesting is typical in Objective-C.

Car *myCar = [[Car alloc] init];

The “allocate followed by init” pattern you see here represents the most common way to instantiate a new object. The class Car performs the alloc method. It allocates a new block of memory sufficient to store all the instance variables listed in the class definition, zeroes out any instance variables, and returns a pointer. The newly allocated block is called an instance and represents a single object in memory.

Some classes, like views, use specialized initializers such as initWithFrame:. You can write custom ones like initWithMake: andModel: andYear:. The pattern of allocation followed by initialization to create new objects holds universally. You create the object in memory and then you preset any critical instance variables.

Memory Allocation

In this example, the memory allocated is 16 bytes in size. Notice that both make and model are pointers, as indicated by the asterisk. In Objective-C, object variables point to the object itself. The pointer is 4 bytes in size. So sizeof(myCar) returns 4. The object consists of two 4-byte pointers, one integer, plus one additional field that does not derive from the Car class.

That extra field is from the NSObject class. Notice NSObject at the right of the colon next to the word Car in the class definition of Listing 3-1. NSObject is the parent class of Car, and Car inherits all instance variables and methods from this parent. That means that Car is a type of NSObject and any memory allocation needed by NSObject instances is inherited by the Car definition. So that’s where the extra 4 bytes come from.

The final size of the allocated object is 16 bytes in total. That size includes two 4-byte NSString pointers, one 4-byte int, and one 4-byte allocation inherited from NSObject. You can easily print out the size of objects using C’s sizeof function. This code uses standard C printf statements to send text information to the console. printf commands work just as well in Objective-C as they do in ANSI C.

image

Releasing Memory

In C, you allocate memory with malloc() or a related call and free that memory with free(). In Objective-C, you allocate memory with alloc and free it with release. (In Objective-C, you can also allocate memory a few other ways, such as by copying other objects.)

[object release];
[myCar release];

As discussed in Chapter 2, “Building Your First Project,” releasing memory is a little more complicated than in standard C. That’s because Objective-C uses a reference-counted memory system. Each object in memory has a retain count associated with it. You can see that retain count by sending retainCount to the object. Every object is created with a retain count of 1. Sending release reduces that retain count by one. When the retain count for an object reaches zero, it is released into the general memory pool.

image

Sending messages to freed objects will crash your application. When the second printf executes, the retainCount message is sent to the already-freed myCar. This creates a memory access violation, terminating the program.

image

There is no garbage collection on the iPhone. As a developer, you must manage your objects. Keep them around for the span of their use and free their memory when you are finished. Read more about basic memory management strategies later in this chapter.

Methods, Messages, and Selectors

In standard C, you’d perform two function calls to allocate and initialize data. Here is how that might look, in contrast to Objective-C’s [[Car alloc] init] statement.

Car *myCar = malloc(sizeof(Car));
init(myCar);

Objective-C doesn’t use function_name(argument) syntax. Instead, you send messages to objects using square brackets. Messages tell the object to perform a method. It is the object’s responsibility to implement that method and produce a result. The first item within the brackets is the receiver of the message, the second item is a method name, and possibly some arguments to that method that together define the message you want sent. In C, you might write

printCarInfo(myCar);

but in Objective-C, you say:

[myCar printCarInfo];

Despite the difference in syntax, methods are basically functions that operate on objects. They are typed using the same types available in standard C. Unlike function calls, Objective-C places limits on who can implement and call methods. Methods belong to classes. And the class interface defines which of these are declared to the outside world.

Dynamic Typing

Objective-C uses dynamic typing in addition to static typing. Static typing restricts a variable declaration to a specific class at compile time. With dynamic typing, the runtime system, not the compiler, takes responsibility for asking objects what methods they can perform and what class they belong to. That means you can choose what messages to send and which objects to send them to as the program runs. This is a powerful feature, one that is normally identified with interpreted systems like Lisp. You can choose an object, programmatically build a message, and send the message to the object all without knowing which object will be picked and what message will be sent at compile time.

With power, of course, comes responsibility. You can only send messages to objects that actually implement the method described by that selector (unless that class can handle messages that don’t have implementations by implementing Objective-C invocation forwarding, which is discussed at the end of this chapter). Sending printCarInfo to an array object, for example, causes a runtime error and crashes the program. Arrays do not define that method. Only objects that implement a given method can respond to the message properly and execute the code that was requested.

image

During compilation, Objective-C performs object message checks using static typing. The array definition in Figure 3-1 is declared statically, telling the compiler that the object in question is of type (NSArray *). When the compiler finds objects that may not be able to respond to the requested methods, it issues warnings.

Figure 3-1 Xcode’s Objective-C issues warnings when it finds a method that does not appear to be implemented by the receiver.

image

These warnings do not make the compilation fail, and it’s possible that this code could run without error if NSArray implemented printCarInfo and did not declare that implementation in its published interface. Since NSArray does not, in fact, implement this method, running this code produces the actual runtime crash shown previously.

Objective-C’s dynamic typing means you can point to the same kind of object in several different ways. Although array was declared as a statically typed (NSArray *) object, that object uses the same internal object data structures as an object declared as id. The id type can point to any object, regardless of class, and is equivalent to (NSObject *). This following assignment is valid and does not generate any warnings at compile time.

image

To further demonstrate, consider a mutable array. The NSMutableArray class is a subclass of NSArray. The mutable version offers arrays that you can change and edit. Creating and typing a mutable array but assigning it to an array pointer compiles without error. Although anotherArray is statically typed as NSArray, creating it in this way produces an object at runtime that contains all the instance variables and behaviors of the mutable array class.

image

What produces a warning here is not the creation and assignment. It’s the use. Sending addObject: to anotherArray uses our knowledge that the array is, in fact, mutable despite the fact that it is statically typed as (NSArray *). That’s something the compiler does not understand. This use generates a compile-time warning, namely “‘NSArray’ may not respond to ‘-addObject:’” At runtime, however, the code works without error.

While assigning a child class object to a pointer of a parent class generally works at runtime, it’s far more dangerous to go the other way. A mutable array is a kind of array. It can receive all the messages that arrays do. Not every array, on the other hand, is mutable. Sending the addObject: message to a regular array is lethal. Doing so bombs at runtime, as arrays do not implement that method.

image

The code seen here produces just one warning, at the line where the standard array object is assigned to the mutable array pointer, namely “assignment from distinct Objective-C type.” Parent-to-child assignments do not generate this warning. Child-to-parent assignments do. So do assignments between completely unrelated classes. Do not ignore this warning; fix your code. Otherwise, you’re setting yourself up for a runtime crash. Because Objective-C is a compiled language that uses dynamic typing, it does not perform many of the runtime checks that interpreted object-oriented languages do.

Note

In Xcode, you can set the compiler to treat warnings as errors by setting the GCC_TREAT_WARNINGS_AS_ERRORS flag in the Project Info > Build > User-Defined panel. Because Objective-C is so dynamic, the compiler cannot catch every problem that might crash at runtime the way static language compilers can. So pay special attention to warnings and try to eliminate them.

Inheriting Methods

As with data, objects inherit method implementations as well as instance variables. A Car is a kind of NSObject, so it can respond to all the messages that an NSObject responds to. That’s why myCar can be allocated and initialized with alloc and init. These methods are defined by NSObject. Therefore, they can be used by any instance of Car, which is derived from the NSObject class.

Similarly, NSMutableArray instances are a kind of NSArray. All array methods can be used by mutable arrays, their child class. You can count the items in the array, pull an object out by its index number, and so forth.

A child class may override a parent’s method implementation, but it can’t negate that the method exists. Child classes always inherit the full behavior and state package of their parents.

Declaring Methods

As Listing 3-1 showed, a class interface defines the instance variables and methods that a new class adds to its parent class. This interface is normally placed into a header file, which is named with a .h extension. The interface from Listing 3-1 declared three methods, namely

image

These three methods, respectively, return void, void, and int. Notice the dash that starts the method declaration. It indicates that the methods are implemented by object instances. For example, you call [myCar year] and not [Car year]. The latter sends a message to the Car class rather than an actual car object. A discussion about class methods (indicated by “+” rather than “-”) follows later in this section.

As mentioned earlier, methods calls can be complex. The following invocation sends a method request with three parameters. The parameters are interspersed inside the method invocation. The name for the method, that is, its selector, is setMake: andModel: andYear:. The three colons indicate where parameters should be inserted. The types for each parameter are specified in the interface after the colons, namely (NSString *), NSString *), and (int). As this method returns void, the results are not assigned to a variable.

[myCar setMake:@"Ford" andModel:@"Prefect" andYear:1946];

Implementing Methods

Together, a method file and a header file pair store all the information needed to implement a class and announce it to the rest of an application. The implementation section of a class definition provides the code that implements functionality. This source is usually placed in a .m (m is for “method”) file.

Listing 3-2 shows the implementation for the Car class example. It codes all three methods declared in the header file from Listing 3-1 and adds a fourth. This extra method redefines init. The Car version of init sets the make and model of the car to nil, which is the NULL pointer for Objective-C objects. It also initializes the year of the car to 1901.

Listing 3-2 The Car Class Implementation (Car.m)

image

The special variable self refers to the object that is implementing the method. That object is also called the receiver, that is, the object that receives the message. This variable is made available by the underlying Objective-C runtime system. In this case, self refers to the current instance of the Car class. Calling [self message] tells Objective-C to send a message to the object that is currently executing the method.

Several things are notable about the init method seen here. First, the method returns a value, which is typed to (id). As mentioned earlier in this chapter, the id type is more or less equivalent to (NSObject *), although it’s theoretically slightly more generic than that. It can point to any object of any class (including Class objects themselves). You return results the same way you would in C, using return. The goal of init is to return a properly initialized version of the receiver via return self.

Second, the method calls [super init]. This tells Objective-C to send a message to a different implementation, namely the one defined in the object’s superclass. The superclass of Car is NSObject, as shown in Listing 3-1. This call says “please perform the initialization that is normally done by my parent class before I add my custom behavior.”

Finally, notice the check for if (!self). In rare instances, memory issues arise. In such a case, the call to [super init] returns nil. If so, this init method returns before setting any instance variables. Since a nil object does not point to allocated memory, you cannot access instance variables within nil.

As for the other methods, they use year, make, and model as if they were locally declared variables. As instance variables, they are defined within the context of the current object and can be set and read as shown in this example. The UTF8String method that is sent to the make and model instance variables converts these NSString objects into C strings, which can be printed using the %s format specifier.

Note

You can send any message to nil, for example, [nil anyMethod]. The result of doing so is, in turn, nil. (Or, more accurately, 0 casted as nil.) In other words, there is no effect. This behavior lets you nest method invocations with a failsafe should any of the individual methods fail and return nil. If you were to run out of memory during an allocation with [[Car alloc] init], the init message would be sent to nil, allowing the entire alloc/init request to return nil in turn.

Class Methods

Class methods are defined using a plus (+) prefix rather than a hyphen (-). They are declared and implemented in the same way as instance methods. For example, you might add the following method declaration to your interface:

+ (NSString *) motto;

and code it up in your implementation:

image

Class methods differ from instance methods in that they generally cannot use state. That is, they have no access to instance variables because those elements are only created when objects are allocated from memory.

So why use class methods at all? The answer is threefold. First, class methods produce results without having to instantiate an actual object. This motto method produces a hard-coded result that does not depend on access to variables. Convenience methods like this often have a better place as classes rather than instance methods.

You might imagine a class that handles geometric operations. The class could implement a conversion between radians and angles without needing an instance, for example, [GeometryClass convertAngleToRadians:theta];. Simple C functions declared in header files also provide a good match to this need.

The second reason is that class methods can hide a singleton. Singletons refer to statically allocated instances. The iPhone SDK is full of these. For example, [UIApplication sharedApplication] returns a pointer to the singleton object that is your application. [UIDevice currentDevice] retrieves an object representing the hardware platform you’re working on.

Combining a class method with a singleton lets you access that static instance anywhere in your application. You don’t need a pointer to the object or an instance variable that stores it. The class method pulls that object’s reference for you and returns it on demand.

Third, class methods tie into memory management schemes. Consider allocating a new NSArray. You do so via [[NSArray alloc] init], or you can use [NSArray array]. This latter class method returns an array object that has been initialized and set for autorelease. As you read about later in this chapter, Apple has provided a standard about class methods that create objects. They always return those objects to you already autoreleased. Because of that, this class method pattern is a fundamental part of the standard iPhone memory management system.

Fast Enumeration

Fast enumeration was introduced in Objective-C 2.0 and offers a simple and elegant way to enumerate through collections like arrays and sets. It adds a for-loop that iterates through the collection using concise for/in syntax. The enumeration is very efficient, running quickly. It is also safe. Attempts to modify the collection as it’s being enumerated raise a runtime exception.

image

Note

Use caution when using methods like arrayWithObjects: or dictionaryWithKeysAndValues: as being unnecessarily error-prone. Developers often use these methods with instance variables without first checking that these values are non-nil.

Class Hierarchy

In Objective-C, each new class is derived from an already-existing class. The Car class described in Listings 3-1 and 3-2 is formed from NSObject, the root class of the Objective-C class tree. Each subclass adds or modifies state and behavior that it inherits from its parent, also called its superclass. The Car class adds several instance variables and methods to the vanilla NSObject it inherits.

Figure 3-2 shows some of the classes found on the iPhone and how they relate to each other in the class hierarchy. Strings and arrays descend from NSObject as does the UIResponder class. UIResponder is the ancestor of all onscreen iPhone elements. Views, labels, text fields, and sliders are children, grandchildren, or other descendants of UIResponder and NSObject.

Figure 3-2 All Cocoa Touch classes are descended from NSObject, the root of the class hierarchy tree.

image

Every class other than NSObject descends from other classes. UITextField is a kind of UIControl, which is in turn a kind of UIView, which is a UIResponder, which is an NSObject. Building into this object hierarchy is what Objective-C is all about. Child classes can

• Add new instance variables that are not allocated by their parent, also called the superclass. The Car class adds three: the make and model strings, and the year integer.

• Add new methods that are not defined by the parent. Car defines several new methods, letting you set the values of the instance variables and print out a report about the car.

• Override methods that the parents have already defined. The Car class’s init method overrides NSObject’s version. When sent an init message, a car object runs its version, not NSObject’s. At the same time, the code for init makes sure to call NSObject’s init method via [super init]. Referencing a parent’s implementation, while extending that implementation, is a core part of the Objective-C design philosophy.

Logging Information

Now that you’ve read the basics about classes and objects, it’s important to understand how to log information about them. In addition to printf, Objective-C offers a fundamental logging function called NSLog. This function works like printf and uses a similar format string, but it outputs to stderr instead of stdout. NSLog also uses an NSString format string rather than a C string one.

NSStrings are declared differently than C strings. They are prepended with the @ (at) symbol. A typical NSString looks @“like this”; the equivalent C string looks “like this”, omitting the @. Whereas C strings refer to a pointer to a string of bytes, NSStrings are objects. You can manipulate a C string by changing the values stored in each byte. NSStrings are immutable; you cannot access the bytes to edit them, and the actual string data is not stored within the object.

image

In addition to using the standard C format specifiers, NSLog introduces an object specifier, %@, which lets you print objects. This allows you to transform

printf("Make: %s ", [make UTF8String]);

into

NSLog(@"Make: %@", make);

Table 3-1 shows some of the most common format specifiers. This is far from an exhaustive list, so consult Apple’s String Programming Guide for Cocoa for more details.

Table 3-1 Common String Format Specifiers

image

Notice that NSLog does not require a hard-coded return character. It automatically appends a new line when used. What’s more it adds a time stamp to every log, so the results of the NSLog invocation shown previously look something like this:

2009-05-07 14:19:08.792 HelloWorld[11197:20b] Make: Ford

Nearly every object converts itself into a string via the description message. NSLog uses description to show the contents of objects formatted with %@. This returns an NSString with a textual description of the receiver object. You can describe objects outside of NSLog by sending them the same description method. This is particularly handy for use with printf and fprintf, which cannot otherwise print objects.

fprintf(stderr, "%s ", [[myCar description] UTF8String]);

Another useful logging function is called CFShow(). It takes one argument, an object, and prints out a snapshot description of that object to stderr.

CFShow(make);

Like NSLog, CFShow sends description to the objects it displays. Unlike NSLog, however, CFShow does not clutter your debugging console with time stamps, so it appeals to anyone who prefers to skip that extra information. CFShow doesn’t require format strings, which simplifies adding them to code, but they can only be used with objects. You cannot CFShow an integer or float.

Properties

Properties expose class variables and methods to outside use through what are called accessor methods, that is, methods that access information. Using properties might sound redundant. After all, the class definition shown in Listing 3-1 already announces public methods. So why use properties? It turns out that there are advantages to using properties over hand-built methods, not the least of which are dot notation and memory management.

Dot Notation

Dot notation allows you to access object information without using brackets. Instead of calling [myCar year] to recover the year instance variable, you use myCar.year. While this may look as if you’re directly accessing the year instance variable, you’re not. Properties always invoke methods. These in turn can access an object’s data. So you’re not, strictly speaking, breaking an object’s encapsulation as properties rely on these methods to bring data outside the object.

Due to method hiding, properties simplify the look and layout of your code. For example, you can access properties to set a table’s cell text via

myTableViewCell.textLabel.text = @"Hello World";

rather than the more cumbersome

[[myTableViewCell textLabel] setText:@"Hello World"];

The property version of the code is more readable and ultimately easier to maintain.

Properties and Memory Management

Properties simplify memory management. You can create properties that automatically retain instance variables for the lifetime of your objects and release them when you set those variables to nil. Setting a retained property ensures that memory will not be released until you say so.

The arrayWithObjects: method normally returns an autoreleased object, whose memory is deallocated at the end of the event loop cycle. (See Chapter 1, “Introducing the iPhone SDK,” for details about autorelease pools. A deeper discussion about memory management follows later in this chapter.) Assigning the array to a retained property means that the array will stick around indefinitely.

self.colors = [NSArray arrayWithObjects:
      @"Gray", @"Silver", @"Black"];

When you’re done using the array and want to release its memory, set the property to nil. This approach works because Objective-C knows how to synthesize accessor methods, creating properly managed ways to change the value of an instance variable. You’re not really setting a variable to nil. You’re actually telling Objective-C to run a method that releases any previously set object and then sets the instance variable to nil. All this happens behind the scenes. From a coding point of view, it simply looks as if you’re assigning a variable to nil.

self.colors = nil;

As a rule, do not send release directly to retained properties, that is, [self.colors release]. This does not affect the colors instance variable assignment, which now points to memory that is likely deallocated. When you next assign an object to the retained property, the memory pointed to by self.colors will receive an additional release message, likely causing a crash due to a double-free exception.

Creating Properties

There are two basic styles of properties, readwrite and readonly. Read-write properties, which are the default, let you modify the values you access; read-only properties do not. The two kinds of accessor methods you must provide are called setters and getters.

Setters set information; getters retrieve information. You can define these with arbitrary method names or you can use the standard Objective-C conventions: The name of the instance variable retrieves the object, while the name prefixed with set, sets it. Objective-C can even synthesize these methods for you. For example, if you declare a property such as the Car class’s year in your class interface as such

@property int year;

and then synthesize it in your class implementation with

@synthesize year;

you can read and set the instance variable with no further coding. Objective-C builds two methods that get the current value (that is, [myCar year]) and sets the current value (that is, [myCar setYear:1962]) and adds the two dot notation shortcuts:

myCar.year = 1962;
NSLog(@"%d", myCar.year);

To build a read-only property, declare it in your interface using the readonly attribute. Read-only properties use getters without setters. For example, here’s a property that returns a formatted text string with car information:

@property (readonly) NSString *carInfo;

Although Objective-C can synthesize read-only properties, you can also build the getter method by hand and add it to your Class implementation. This method returns a description of the car via stringWithFormat:, which uses a format string a la sprintf to create a new string.

image

This method now becomes available for use via dot notation, for example, CFShow(myCar.carInfo);.

If you choose to synthesize a getter for a read-only property, you should use care in your code. Inside your implementation file, make sure you assign the instance variable for that property without dot notation. Imagine that you declared model as a read-only property. You could assign model with

model = @"Prefect";

but not with

self.model = @"Prefect";

The latter use attempts to call setModel:, which is not defined for a read-only property.

Creating Custom Getters and Setters

Although Objective-C automatically builds methods when you @synthesize properties, you may skip the synthesis. You can create those methods yourself. For example, you could build methods as simple as these. Notice the capitalization of the second word in the set method. By convention, Objective-C expects setters to use a method named setInstance: where the first letter of the instance variable name is capitalized.

image

When building your own setters and getters, you might add some basic memory management. The following methods retain new items and release previous values.

image

Note

In the remote case that newModel is somehow a child of model, calling [model release] may free the memory of new model. For that reason, a more complete setter method retains newModel before calling [model release].

Or you could go even further by building more complicated routines that generate side effects upon assignment and retrieval. For example, you might keep a count of the number of times the value has been retrieved or changed, or send in-app notifications to other objects. The Objective-C compiler remains happy so long as it finds, for any property, a getter (typically named the same as the property name) and a setter (usually setName: where name is the name of the property). What’s more, you can bypass any Objective-C naming conventions by specifying setter and getter names in the property declaration. This declaration creates a new Boolean property called forSale and declares a custom getter/setter pair. As always, you add any property declarations to the class interface.

@property (getter=isForSale, setter=setSalable:) BOOL forSale;

Then synthesize the methods as normal in the class implementation. The implementation is typically stored in the .m file that accompanies the .h header file.

@synthesize forSale;

Using this approach creates both the normal setter and getter via dot notation plus the two custom methods, isForSale and setSalable:. Oddly, while you can use dot notation to assign and retrieve forSale, you cannot use the equivalent methods, and you cannot use the customized setter in dot notation. Here is how the usage breaks down.

image

Property Attributes

In addition to readwrite and readonly attributes, you can specify whether a property is retained and/or atomic. The default behavior for properties is assign. Assignment acts exactly as if you’d assigned a value to an instance variable. There’s no special retain/release behavior associated with the property, but by making it a property you expose the variable outside the class via dot notation. A property that’s declared

@property NSString *make;

uses the assign behavior.

Setting the property’s attribute to retain does two things. First, it retains the passed object upon assignment. Second, it releases the previous value before a new assignment is made. Using the retain attribute introduces the memory management advantages discussed in the previous section. To create a retained property, add the attribute between parentheses in the declaration:

@property (retain) NSString *make;

A third attribute called copy sends a copy message to the passed object, retains it, and releases any previous value.

@property (copy) NSString *make;

You can also retain the object as you assign it.

myCar.make = @"Ford";
[myCar.make retain];

When you develop in a multithreaded environment, you want to use atomic methods. Xcode synthesizes atomic methods to automatically lock objects before they are accessed or modified and unlock them after. This ensures that setting or retrieving an object’s value is performed fully regardless of concurrent threads. There is no atomic keyword. All methods are synthesized atomically by default. You can, however, state the opposite, allowing Objective-C to create accessors that are nonatomic.

@property (nonatomic, retain) NSString *make;

Marking your properties nonatomic does speed up access, but you might run into problems should two competing threads attempt to modify the same property at once. Atomic properties, with their lock/unlock behavior, ensure that an object update completes from start to finish before that property is released to another read or change.

Simple Memory Management

Memory management comes down to two simple rules. At creation, every object has a retain count of one. At release, every object has a retain count of zero. It is up to you as a developer to manage an object’s retention over its lifetime. You should ensure that it moves from start to finish without being prematurely released and guarantee that it does finally get released when it is time to do so. Complicating matters is Objective-C’s autorelease pool. If some objects are autoreleased and others must be released manually, how do you best control your objects? Here’s a quick and dirty guide to getting your memory management right.

Creating Objects

Any time you create an object using the alloc/init pattern, you build it with a retain count of one. It doesn’t matter which class you use or what object you build, alloc/init produces a +1 count.

id myObject = [[SomeClass alloc] init];

For locally scoped variables, if you do not release the object before the end of a method, the object leaks. Your reference to that memory goes away, but the memory itself remains allocated. The retain count remains at +1.

image

The proper way to use an alloc/init pattern is to create, use, and then release. Releasing brings the retain count down to 0. When the method ends, the object is deallocated.

image

Autorelease objects do not require an explicit release statement for locally scoped variables. (In fact, avoid doing so to prevent double-free errors that will crash your program.) Sending the autorelease message to an object marks it for autorelease. When the autorelease pool drains at the end of each event loop, it sends release to all the objects it owns.

image

By convention, all class object-creation methods return an autoreleased object. The NSArray class method array returns a newly initialized array that is already set for autorelease. The object can be used throughout the method, and its release is handled when the autorelease pool drains.

image

At the end of this method, the autoreleased array can return to the general memory pool.

Creating Autoreleased Objects

As a rule, whenever you ask another method to create an object, it’s good programming practice to return that object autoreleased. Doing so consistently lets you follow a simple rule: “If I didn’t allocate it, then it was built and returned to me as an autorelease object.”

image

This holds especially true for class methods. By convention all class methods that create new objects return autorelease objects. These are generally referred to as convenience methods. Any object that you yourself allocate is not set as autorelease unless you specify it yourself.

image

To create a convenience method at the class level, make sure to define the class with the + prefix instead of - and return the object after sending autorelease to it.

image

Autorelease Object Lifetime

So how long can you use an autorelease object? What guarantees do you have? The hard and fast rule is that the object is yours until the next item in the event loop gets processed. The event loop is triggered by user touches, by button presses, by “time passed” events, and so forth. In human reckoning these times are impossibly short; in the iPhone’s processor frame of reference, they’re quite large. As a more general rule, you can assume that an autoreleased object should persist throughout the duration of your method call.

Once you return from a method, guarantees go out the window. When you need to use an array beyond the scope of a single method or for extended periods of time (for example, you might start a custom run-loop within a method, prolonging how long that method endures), the rules change. You must retain autorelease objects to increase their count and prevent them from getting deallocated when the pool drains; when the autorelease pool calls release on their memory, they’ll maintain a count of at least +1.

Note

Avoid assigning properties to themselves, for example, myCar.colors = myCar.colors. The release-then-retain behavior of properties may cause the object to deallocate before it can be reassigned and re-retained.

Retaining Autorelease Objects

You can send retain to autorelease objects just like any other object. Retaining objects set to autorelease allows them to persist beyond a single method. Once retained, an autorelease object is just as subject to memory leaks as one that you created using alloc/init. For example, retaining an object that’s scoped to a local variable might leak, as shown here.

image

Upon creation, array has a retain count of +1. Sending retain to the object brings that retain count up to +2. When the method ends and the autorelease pool drains, the object receives a single release message; the count returns to +1. From there, the object is stuck. It cannot be deallocated with a +1 count and with no reference left to point to the object, it cannot be sent the final release message it needs to finish its life cycle. This is why it’s critical to build references to retained objects.

By creating a reference, you can both use a retained object through its lifetime and be able to release it when you’re done. Set references via an instance variable (preferred) or a static variable defined within your class implementation. If you want to keep things simple and reliable, use retained properties built from those instance variables. The next section shows you how retained properties work and demonstrates why they provide a solution of choice for developers.

Retained Properties

Retained properties hold onto data that you assign to them and properly relinquish that data when you set a new value. Because of this, they tie in seamlessly to basic memory management. Here’s how you create and use retained properties in your iPhone applications.

First, declare your retained property in the class interface by including the retain keyword between parentheses.

@property (retain) NSArray *colors;

Then synthesize the property methods in your implementation.

@synthesize colors;

When given the @synthesize directive, Objective-C automatically builds routines that manage the retained property. The routines automatically retain an object when you assign it to the property. That behavior holds regardless of whether the object is set as autorelease. When you reassign the property, the previous value is automatically released.

Assigning Values to Retained Properties

When working with retained properties, you need to be aware of two patterns of assignment. These patterns depend on whether you’re assigning an autorelease object. For autorelease style objects, use a simple single assignment. This assignment sets the colors property to the new array and retains it.

myCar.colors = [NSArray arrayWithObjects:
         @"Black", @"Silver", @"Gray", nil];

The array is created and returned as an autorelease object with a count of +1. The assignment to the retained colors property brings the count to +2. Once the current event loop ends, the autorelease pool sends release to the array, and the count drops back to +1.

For normal (nonautorelease) objects, release the object after assigning it. Upon creation, the retain count for a normally allocated object is +1. Assigning the object to a retained property increases that count to +2. Releasing the object returns the count to +1.

image

You often see this pattern of create, assign, release in iPhone development. You might use it when assigning a newly allocated view to a view controller object. For example:

image

These three steps move the object’s retain count from +1 to +2 and back to +1.

A final count of +1 guarantees you that can use an object indefinitely. At the same time, you’re assured that the object deallocates properly when the property is set to a new value and release is called on its prior value. That release brings the count down from +1 to 0, and the object automatically deallocates.

Reassigning a Retained Property

When you’re done using a retained property, regardless of the approach used to create that object, set the property to nil or to another object. This sends a release message to the previously assigned object.

myCar.colors=nil;

If the colors property had been set to an array, as just shown, that array would automatically be sent a release message. Since each pattern of assignment produced a +1 retained object, this reassignment would bring that retain count down from +1 to 0. The object’s life would be over.

Avoiding Assignment Pitfalls

Within a class implementation, it’s handy to use properties to take advantage of this memory management behavior. To take advantage of this, avoid using instance variables directly. Direct assignment like this won’t retain the array or release any previous value. This is a common pitfall for new iPhone developers. Remember the dot notation when accessing the instance variables.

colors = [NSArray arrayWithObjects:
        @"Black", @"Silver", @"Gray", nil];

This same caution holds true for properties defined as assign. Note the following behavior carefully. Although both

@property NSArray *colors;

and

@property (assign) NSArray *colors;

allow you to use dot notation, assignment via these properties does not retain or release objects. Assign properties expose the colors instance variable to the outside world, but they do not provide the same memory management that retain properties do.

Note

As a general rule of thumb, Apple recommends you avoid using properties in your init functions. Instead, use instance variables directly.

High Retain Counts

Retain counts that go and stay above +1 do not necessarily mean you’ve done anything wrong. Consider the following code segment. It creates a view and starts adding it to arrays. This raises the retain count from +1 up to +4.

image

Notice that each array was created using a class convenience method and returns an autoreleased object. The view is set as autorelease, too. Some collection classes like NSArray automatically retain objects when you add them into an array and release them when either the object is removed (mutable objects only) or when the collection is released. This code has no leaks because every one of the four objects is set to properly release itself and its children when the autorelease pool drains.

When release is sent to the three arrays, each one releases the view, bringing the count down from +4 to +1. The final release, sent to the object, brings the count from +1 down to 0, allowing the view to deallocate when this method finishes: no leaks, no further retains, no problems.

Other Ways to Create Objects

You’ve seen how to use alloc to allocate memory. Objective-C offers other ways to build new objects. You can discover these by browsing class documentation as the methods vary by class and framework. As a rule of thumb, if you build an object using any method whose name includes alloc, new, create, or copy, you maintain responsibility for releasing the object. Unlike class convenience methods, methods that include these words generally do not return autoreleased objects.

Sending a copy message to an object, for example, duplicates it. Copy returns an object with a retain count of +1 and no assignment to the autorelease pool. Use copy when you want to duplicate and make changes to an object while preserving the original. Note that for the most part, Objective-C produces shallow copies of collections like arrays and dictionaries. It copies the structure of the collection, and maintains the addresses for each pointer, but does not perform a deep copy of the items stored within.

C-Style Object Allocations

As a superset of C, Objective-C programs for the iPhone often use APIs with C-style object-creation and management. Core Foundation (CF) is a Cocoa Touch framework with C-based function calls. When working with CF objects in Objective-C, you build objects with CFAllocators and often use the CFRelease() function to release object memory.

There are, however, no simple rules. As the following code shows, you may end up using free(), CFRelease(), and custom methods like CGContextRelease() all in the same scope, side-by-side with standard Objective-C class convenience methods like imageWithCGImage:. The function used to create the context object used here is CGBitmapContextCreate() and like most Core Foundation function calls, it does not return an autoreleased object. This code snippet builds a UIImage, the iPhone class that stores image data.

image

Carbon and Core Foundation

Working with Core Foundation comes up often enough that you should be aware of its existence and be prepared to encounter its constructs, specifically as regards to its frameworks. Frameworks are libraries of classes that you can utilize in your application.

Table 3-2 explains the key terms involved. To summarize the issue, early OS X used a C-based framework called Core Foundation to provide a transitional system for developing applications that could run on both Classic Mac systems as well as Mac OS X. Although Core Foundation uses object-oriented extensions to C, its functions and constructs are all C-based, not Objective-C-based.

Table 3-2 Key OS X Development Terms

image

Core Foundation technology lives on through Cocoa. You can and will encounter C-style Core Foundation when programming iPhone applications using Objective-C. The specifics of Core Foundation programming fall outside the scope of this chapter, however, and are best explored separately from learning how to program in Objective-C.

Deallocating Objects

The iPhone uses reference-count managed Objective-C. On the iPhone, there’s no garbage collection and little likelihood there ever will be. Every object cleans up after itself. So what does that mean in practical terms? Here’s a quick rundown of how you end an object’s life, cleaning up its instance variables and preparing it for deallocation.

Instance variables must release retained objects before deallocation. You as the developer must ensure that those objects return to a retain count of 0 before the parent object is itself released. To do this, you implement dealloc, a method automatically called by the runtime system when an object is about to be released. If you use a class with object instance variables (i.e., not just floats, ints, and Bools), you probably need to implement a deallocation method. The basic dealloc method structure looks like this:

image

The method you write should work in two stages. First, clean up any instance variables from your class. Then ask your superclass to perform its cleanup routine. The special super keyword refers to the superclass of the object that is running the dealloc method. How you clean up depends on whether your instance variables are automatically retained.

You’ve read about creating objects, building references to those objects, and ensuring that the objects’ retain counts stay at +1 after creation. Now, you see the final step of the object’s lifetime, namely reducing that count back to 0 so the objects can be deallocated.

Retained Properties

In the case of retained properties, set those properties to nil using dot notation assignment. This calls the custom setter method synthesized by Objective-C and releases any prior object the property has been set to. Assuming that prior object had a retain count of +1, this release brings the count to 0.

self.make = nil;

Variables

When using plain (nonproperty) instance variables or assign style properties, send release at deallocation time. Say, for example, you’ve defined an instance variable called salesman. It might be set at any time during the lifetime of your object. The assignment of salesman might look like this:

image

This assignment style means that salesman could point to an object with a +1 retain count at any time during the object’s lifetime. Therefore in your dealloc method, you must release any object currently assigned to salesman, setting the count to 0.

[salesman release];

A Sample Deallocation Method

Keeping with an expanded Car class that uses retained properties for make, model, and colors, and that has a simple instance variable for salesman, the final deallocation method would look like this. The integer year and the Boolean forSale instance variables are not objects and do not need to be managed this way.

image

Setting a retain count upper limit proves key to making Objective-C memory management work. Few objects should continue to have a retain count greater than +1 after their creation and assignment. By guaranteeing a limit, your final releases in dealloc are assured to bring those counts down to 0.

Cleaning Up Other Matters

The dealloc method offers a perfect place to clean up shop. For example, you might need to dispose of an Audio Toolbox sound or perform other maintenance tasks before the class is released. These tasks almost always relate to legacy Core Foundation, Core Graphics, Core Audio, or similar C-style frameworks.

if (snd) AudioServicesDisposeSystemSoundID(snd);

Think of dealloc as your last chance to tidy up loose ends before your object goes away forever. Whether this involves shutting down open sockets, closing file pointers, or releasing resources, use this method to make sure your code returns state as close to pristine as possible.

Crafting Singletons

The UIApplication and UIDevice classes let you access information about the currently running application and the device hardware it is running on. They do so by offering singletons, that is, a sole instance of a class in the current process. For example, [UIApplication sharedApplication] returns a singleton that can report information about the delegate it uses, whether the application supports shake-to-edit features, what windows are defined by the program, and so forth.

Most singleton objects act as control centers. They coordinate services, provide key information, and direct external access, among other functionality. If you have a need for centralized functionality, like a manager that accesses a Web service, a singleton approach ensures that all parts of your application coordinate with the same central manager.

Building a singleton takes very little code. You define a static shared instance inside the class implementation and add a class method pointing to that instance. In this snippet, which is taken from the tagging example of Chapter 6, “Assembling Views and Animations,” the instance is built the first time it is requested.

image

To use this singleton, call [ViewIndexer sharedInstance]. This returns the shared object and lets you access any behavior that the singleton provides. You can prevent any class from creating a second instance by overriding allocWithZone:. (For most uses this is paranoid overkill.) The @synchronized() directive used here prevents this code from being executed by more than one thread at a time.

image

Categories (Extending Classes)

Objective-C’s built-in capability to expand already-existing classes is one of its most powerful features. This behavioral expansion is called a category. Categories extend class functionality without subclassing. You choose a descriptive expansion name, build a header, and then implement the functionality in a method file. Categories add methods to existing classes even if you did not define that class in the first place and do not have the source code for that class.

To build a category, you declare a new interface. Specify the category name (it’s arbitrary) within parentheses, as you see here. List any new public methods and properties and save the header file. This Orientation category expands the UIDevice class, which is the SDK class responsible for reporting device characteristics including orientation, battery level, and the proximity sensor state. This interface adds a single property to UIDevice, returning a read-only Boolean value. The new isLandscape property reports back whether the device is currently using a landscape orientation.

image

You cannot add new instance variables to a category interface as you could when subclassing. You are instead expanding a class’s behavior, as shown in the source code of Listing 3-3. The code implements the landscape check by looking at the standard UIDevice orientation property.

Listing 3-3 Building an Orientation Category for the UIDevice Class

image

You might use the new property like this.

image

Here, the landscape orientation check integrates seamlessly into the SDK-provided UIDevice class via a property that did not exist prior to expanding the class. Just FYI, UIKit does offer device orientation macros (UIDeviceOrientationIsPortrait and UIDeviceOrientationIsLandscape), but you must pass these an orientation value, which you have to poll from the device.

Note

In addition to adding new behavior to existing classes, categories also let you group related methods into separate files for classes you build yourself. For large, complex classes, this helps increase maintainability and simplifies the management of individual source files. Please note that when you add a category method that duplicates an existing method signature, the Objective-C runtime uses your implementation and overrides the original.

Protocols

Chapter 1 introduced the notion of delegates. Delegates implement details that cannot be determined when a class is first defined. For example, a table knows how to display rows of cells, but it can’t know what to do when a cell is tapped. The meaning of a tapped row changes with whatever application implements that table. A tap might open another screen, or send a message to a Web server, or perform any other imaginable result. Delegation lets the table communicate with a smart object that is responsible for handling those taps but whose behavior is written at a completely separate time from when the table class itself is created.

Delegation basically provides a language that mediates contact between an object and its handler. A table tells its delegate “I have been tapped,” “I have scrolled,” and other status messages. The delegate then decides how to respond to these messages, producing updates based on its particular application semantics.

Data sources operate the same way, but instead of mediating action responses, data sources provide data on demand. A table asks its data source, “What information should I put into cell 1 and cell 2?” The data source responds with the requested information. Like delegation, data sourcing lets the table place requests to an object that is built to understand those demands.

In Objective-C, both delegation and data sourcing are produced by a system called protocols. Protocols define a priori how one class can communicate with another. They contain a list of methods that are defined outside any class. Some of these methods are required. Others are optional. Any class that implements the required methods is said to conform to the protocol.

Defining a Protocol

Imagine, if you would, a jack-in-the box toy. This is a small box with a handle. When you turn the crank, music plays. Sometimes a puppet (called the “jack”) jumps out of the box. Now imagine implementing that toy (or a rough approximation) in Objective-C. The toy provides one action, turning the crank, and there are two possible outcomes: the music or the jack.

Now consider designing a programmatic client for that toy. It could respond to the outcomes, perhaps, by gradually increasing a boredom count when more music plays or reacting with surprise when the jack finally bounces out. From an Objective-C point of view, your client needs to implement two responses: one for music, another for the jack. Here’s a client protocol you might build.

image

This protocol declares that to be a client of the toy, you must respond to music playing and the jack jumping out of the box. Listing these methods inside an @protocol container defines the protocol. All the methods listed here are required unless you specifically declare them as @optional, as you read about in the next sections.

Incorporating a Protocol

Next, imagine designing a class for the toy itself. It offers one action, turning the crank, and requires a second object that implements the protocol, in this case called client. This class interface specifies that the client needs to be some kind of object (id) that conforms to the JackClient protocol (<JackClient>). Beyond that, the class does not know at design time what kind of object will provide these services.

image

Adding Callbacks

Callbacks connect the toy class to its client. Since the client must conform to the JackClient protocol, you can send jackDidAppear and musicDidPlay messages to the object and they will compile without error. The protocol ensures that the client implements these methods. In this code, the callback method is selected randomly. The music plays approximately nine out of every ten calls, sending musicDidPlay to the client.

image

Declaring Optional Callbacks

Protocols include two kinds of callbacks, required and optional. By default, callbacks are required. A class that conforms to the protocol must implement those methods or they produce a compiler warning. You can use the @required and @optional keywords to declare a protocol to be of one form or the other. Any methods listed after an @required keyword are required; after an @optional keyword, they are optional. Your protocol can grow complex accordingly.

image

In practice, using more than a single @optional keyword is overkill. The same protocol can be declared more simply. When you don’t use any optional items, skip the keyword entirely. Notice the <NSObject> declaration here. It’s required to effectively implement optional protocols. It says that a JackClient object conforms to and will be a kind of NSObject.

image

Implementing Optional Callbacks

Optional methods let the client choose whether to implement a given protocol method. They reduce the implementation burden on whoever writes that client but add a little extra work to the class that hosts the protocol definition. When you are unsure whether a class does or does not implement a method, you must test before you send a message. Fortunately, Objective-C and the NSObject class make it easy to do so.

image

NSObject provides a respondsToSelector: method, which returns a Boolean YES if the object implements the method or NO otherwise. By declaring the client with <NSObject>, you tell the compiler that the client can handle this method, allowing you to check the client for conformance before sending the message.

Conforming to a Protocol

Classes include protocol conformance in interface declarations. A view controller that implements the JackClient protocol announces it between angle brackets. A class might conform to several protocols. Combine these within the brackets, separating protocol names with commas.

image

Declaring the JackClient protocol lets you assign the host’s client property. The following code compiles without error because the class for self was declared in conformance with JackClient.

self.jack = [JackInTheBox jack];
self.jack.client = self;

Had you omitted the protocol declaration in your interface, this assignment would produce an error at compile time.

Once you include that protocol between the angle brackets, you must implement all required methods in your class. Omitting any of them produces the kind of compile-time warnings shown in Figure 3-3. The compiler tells you which method is missing and what protocol that method belongs to.

Figure 3-3 You must implement all required methods to conform to a protocol. Objective-C warns about incomplete implementations.

image

The majority of protocol methods in the iPhone SDK are optional. Both required and optional methods are detailed exhaustively in the developer documentation. Note that protocols are documented separately from the classes they support. For example, Xcode documentation provides three distinct UITableView reference pages: one for the UITableView class, one for the UITableViewDelegate protocol, and another for the UITableViewDataSource protocol.

Foundation Classes

If you’re new to Objective-C, there are a few key classes you absolutely need to be familiar with before moving forward. These include strings, numbers, and collections, and they provide critical application building blocks. The NSString class, for example, provides the workhorse for nearly all text manipulation in Objective-C. However it, like other fundamental classes, is not defined in Objective-C itself. It is part of the Foundation framework, which offers nearly all the core utility classes you use on a day-to-day basis.

Foundation provides over a dozen kinds of object families and hundreds of object classes. These range from value objects that store numbers and dates, to strings that store character data, and collections that store other objects, to classes that access the file system and retrieve data from URLs. Foundation is often referred to (slightly inaccurately) as Cocoa. (Cocoa and its iPhone equivalent Cocoa Touch actually include all the frameworks for OS X programming.) To master Foundation is to master Objective-C programming, and thorough coverage of the subject demands an entire book of its own.

As this section cannot offer an exhaustive introduction to Foundation classes, you’re about to be introduced to a quick and dirty survival overview. Here are the classes you need to know about and the absolutely rock-core ways to get started using them. You find extensive code snippets that showcase each of the classes to give you a jumping-off point if, admittedly, not a mastery of the classes involved.

Strings

Cocoa strings store character data, just as their cousins the (char *) C strings do. They are, however, objects and not byte arrays. Unlike C, the core NSString class is immutable in Cocoa. That is, you can use strings to build other strings, but you can’t edit the strings you already own. String constants are delineated by quote marks and the @ character. Here is a typical string constant, which is assigned to a string variable.

NSString *myString = @"A string constant";

Building Strings

You can build strings using formats, much as you would using sprintf. If you’re comfortable creating printf statements, your knowledge transfers directly to string formats. Use the %@ format specifier to include objects in your strings. String format specifiers are thoroughly documented in the Cocoa String Programming Guide, available via Xcode’s documentation window (Command-Option-?). The most common formats are listed in Table 3-1.

NSString *myString = [NSString stringWithFormat:
        @"The number is %d", 5];

To create new strings, you can append strings together. This call outputs “The number is 522”. It creates a new instance built from other strings.

NSLog(@"%@", [myString stringByAppendingString:@"22"]);

Appending formats provides even more flexibility. You specify the format string and the components that build up the result.

NSLog(@"%@", [myString stringByAppendingFormat:@"%d", 22]);

Length and Indexed Characters

Every string can report its length (via length) and produce an indexed character on demand (via characterAtIndex:). The two calls shown here output 15 and e, respectively, based on the previous @“The number is 5” string. Cocoa characters use the unichar type, which store Unicode-style characters.

NSLog(@"%d", myString.length);
printf("%c", [myString characterAtIndex:2]);

Converting to and from C Strings

The realities of normal C programming often crop up despite working in Objective-C. Being able to move back and forth between C strings and Cocoa strings is an important skill. Convert an NSString to a C string either by sending UTF8String or cStringUsingEncoding:. These are equivalent, producing the same C-based bytes.

printf("%s ", [myString UTF8String]);
printf("%s ", [myString cStringUsingEncoding: NSUTF8StringEncoding]);

You can also go the other way and transform a C string into an NSString by using stringWithCString: encoding:. The samples here use UTF-8 encoding, but Objective-C supports a large range of options, including ASCII, Japanese, Latin, Windows-CP1251, and so forth.

NSLog(@"%@", [NSString stringWithCString:"Hello World"
          encoding: NSUTF8StringEncoding]);

Writing Strings to and Reading Strings from Files

Writing to and reading strings from the local file system offers a handy way to save and retrieve data. This snippet shows how to write a string to a file.

image

The path for the file is NSHomeDirectory(), a function that returns a string with a path pointing to the application sandbox. Notice the special append method that properly appends the Documents/file.txt subpath.

In Cocoa, most file access routines offer an atomic option. When you set the atomically parameter to YES, the iPhone writes the file to a temporary auxiliary and then renames it into place. Using an atomic write ensures that the file avoids corruption.

The request shown here returns a Boolean, namely YES if the string was written, or NO if it was not. Should the write request fail, this snippet logs the error using a language-localized description. It uses an instance of the NSError class to store that error information and sends the localizedDescription selector to convert the information into a human-readable form. Whenever iPhone methods return errors, use this approach to determine which error was generated.

Reading a string from a file follows a similar form but does not return the same Boolean result. Instead, check to see whether the returned string is nil, and if so display the error that was returned.

image

Accessing Substrings

Cocoa offers a number of ways to extract substrings from strings. Here’s a quick review of some typical approaches. As you’d expect, string manipulation is a large part of any flexible API, and Cocoa offers many more routines and classes to parse and interpret strings than the few listed here. This quick NSString summary skips any discussion of NSScanner, NSXMLParser, and so forth.

Converting Strings to Arrays

You can convert a string into an array by separating its components across some repeated boundary. This sample chops the string into individual words by splitting around spaces. The spaces are discarded, leaving an array that contains each number word.

image

Requesting Indexed Substrings

You can request a substring from the start of a string to a particular index, or from an index to the end of the string. These two examples return @“One Two” and @“Two Three Four Five Six Seven”, respectively, using the to and from versions of the indexed substring request. As with standard C, array and string indices start at 0.

image

Generating Substrings from Ranges

Ranges let you specify exactly where your substring should start and stop. This snippet returns @“Tw”, starting at character 4 and extending 2 characters in length. NSRange provides a structure that defines a section within a series. You use ranges with indexed items like strings and arrays.

image

Search and Replace with Strings

With Cocoa, you can easily search a string for a substring. Searches return a range, which contain both a location and a length. Always check the range location. The location NSNotFound means the search failed. This returns a range location of 18, with a length of 4.

image

Once you’ve found a range, you can replace a subrange with a new string. The replacement string does not need to be the same length as the original, thus the result string may be longer or shorter than the string you started with.

image

A more general approach lets you replace all occurrences of a given string. This snippet produces @“One * Two * Three * Four * Five * Six * Seven” by swapping out each space for a space-asterisk-space pattern.

image

Changing Case

Cocoa provides three simple methods that change a string’s case. Here, these three examples produce a string all in uppercase, all in lowercase, and one where every word is capitalized (“Hello World. How Do You Do?”). Because Cocoa supports case-insensitive comparisons, you rarely need to apply case conversions when testing strings against each other.

image

Testing Strings

The iPhone offers many ways to compare and test strings. The three simplest check for string equality and match against the string prefix (the characters that start the string) and suffix (those that end it). More complex comparisons use NSComparisonResult constants to indicate how items are ordered compared with each other.

image

Extracting Numbers from Strings

Convert strings into numbers by using a value method. These examples return 3, 1, 3.141592, and 3.141592, respectively.

image

Mutable Strings

The NSMutableString class is a subclass of NSString. It offers you a way to work with strings whose contents can be modified. Once instantiated, you can append new contents to the string, allowing you to grow results before returning from a method. This sample displays “Hello World. The results are in now.”

image

Numbers and Dates

Foundation offers a large family of value classes. Among these are numbers and dates. Unlike standard C floats, integers, and so forth, these elements are all objects. They can be allocated and released, and used in collections like arrays, dictionaries, and sets. The following examples show numbers and dates in action, providing a basic overview of these classes.

Working with Numbers

The NSNumber class lets you treat numbers as objects. You can create new NSNumber instances using a variety of convenience methods, namely numberWithInt:, numberWithFloat:, numberWithBool:, and so forth. Once set, you extract those values via intValue, floatValue, boolValue, and so on, and use normal C-based math to perform your calculations.

You are not limited to extracting the same data type an object was set with. You can set a float and extract the integer value, for example. Numbers can also convert themselves into strings.

image

One of the biggest reasons for using NSNumber objects rather than ints, floats, and so forth, is that you can use them with Cocoa routines and classes. For example, you cannot set a user default (that is, a preference value) to, say, the integer 23, as in “You have used this program 23 times.” You can, however, store an object [NSNumber numberWithInt:23] and later recover the integer value from that object to produce the same user message.

Note

The NSDecimalNumber class provides a handy object-oriented wrapper for base-10 arithmetic.

Working with Dates

As with standard C and time(), NSDate objects use the number of seconds since an epoch, that is a standardized universal time reference, to represent the current date. The iPhone epoch was at midnight on January 1, 2001. The standard Unix epoch took place at midnight on January 1, 1970.

Each NSTimeInterval represents a span of time in seconds, stored with subsecond floating-point precision. The following code shows how to create a new date object using the current time and how to use an interval to reference some time in the future (or past).

image

You can compare dates by setting or checking the time interval between them. This snippet forces the application to sleep until 5 seconds into the future and then compares the date to the one stored in date.

image

The standard description method for dates returns a somewhat human-readable string, showing the current date and time.

// Show the date
NSLog(@"%@" [date description]);

To convert dates into fully formatted strings rather than just using the default description, use an instance of NSDateFormatter. You specify the format (for example, YY for two-digit years, and YYYY for four-digit years) using the object’s date format property. A full list of format specifiers is offered in the built-in Xcode documentation. In addition to producing formatted output, this class can also be used to read preformatted dates from strings, although that is left as an exercise for the reader.

image

Timers

When working with time, you may need to request that some action occur in the future. Cocoa offers an easy-to-use timer that triggers at an interval you specify; use the NSTimer class. The timer shown here triggers after one second and repeats until the timer is disabled.

[NSTimer scheduledTimerWithTimeInterval: 1.0f target: self
        selector: @selector(handleTimer:) userInfo: nil repeats: YES];

Each time the timer activates, it calls its target sending the selector message it was initialized with. The callback method takes one argument (notice the single colon), which is the timer itself. To disable a timer, send it the invalidate message; this releases the timer object and removes it from the current runloop.

image

Recovering Information from Index Paths

The NSIndexPath class is used with iPhone tables. It stores the section and row number for a user selection, that is, when a user taps on the table. When provided with index paths, you can recover these numbers via the myIndexPath.row and myIndexPath.section properties. Learn more about this class and its use in Chapter 11, “Creating and Managing Table Views.”

Collections

The iPhone primarily uses three kinds of collections: arrays, dictionaries, and sets. Arrays act like C arrays. They provide an indexed list of objects, which you can recover by specifying which index to look at. Dictionaries, in contrast, store values that you can look up by keys. For example, you might store a dictionary of ages, where Dad’s age is the NSNumber 57, and a child’s age is the NSNumber 15. Sets offer an unordered group of objects and are usually used on the iPhone in connection with recovering user touches from the screen. Each of these classes offers regular and mutable versions, just as the NSString class does.

Building and Accessing Arrays

Create arrays using the arrayWithObjects: convenience method, which returns an autoreleased array. When calling this method, list any objects you want added to the array and finish the list with nil. (If you do not include nil in your list, you’ll experience a runtime crash.) You can add any kind of object to an array, including other arrays and dictionaries. This sample showcases the creation of a three-item array.

NSArray *array = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

The count property returns the number of objects in an array. Arrays are indexed starting with 0, up to one less than the count. Attempting to access [array objectAtIndex:array.count] causes an “index beyond bounds” exception and crashes. So always use care when retrieving objects, making sure not to cross either the upper or lower boundary for the array.

NSLog(@"%d", array.count);
NSLog(@"%@", [array objectAtIndex:0]);

Mutable arrays are editable. The mutable form of NSArray is NSMutableArray. With mutable arrays, you can add and remove objects at will. This snippet copies the previous array into a new mutable one and then edits the array by adding one object and removing another one. This returns an array of [@“One”, @“Two”, @“Four].

image

Whether or not you’re working with mutable arrays, you can always combine arrays to form a new version containing the components from each. No checks are done about duplicates. This code produces a six-item array including one, two, and three from the original array, and one, two, and four, from the mutable array.

NSLog(@"%@", [array arrayByAddingObjectsFromArray:marray]);

Checking Arrays

You can test whether an array contains an object and recover the index of a given object. This code searches for the first occurrence of “Four” and returns the index for that object. The test in the if statement ensures that at least one occurrence exists.

image

Converting Arrays into Strings

As with other objects, sending description to an array returns an NSString that describes an array. In addition, you can use componentsJoinedByString to transform an NSArray into a string. The following code returns @“One Two Three”.

NSArray *array = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
NSLog(@"%@", [array componentsJoinedByString:@" "]);

Building and Accessing Dictionaries

NSDictionary objects store keys and values, enabling you to look up objects using strings. The mutable version of dictionaries, NSMutableDictionary, lets you modify these dictionaries by adding and removing elements on demand. In iPhone programming, you use the mutable class more often the static one, so these examples showcase mutable versions.

Creating Dictionaries

Use the dictionary convenience method to create a new mutable dictionary, as shown here. This returns a new initialized dictionary that you can start to edit. Populate the dictionary using setObject: forKey:.

image

Searching Dictionaries

Searching the dictionary means querying the dictionary by key name. Use objectForKey: to find the object that matches a given key. When a key is not found, the dictionary returns nil. This returns @"1" and nil.

NSLog(@"%@", [dict objectForKey:@"A"]);
NSLog(@"%@", [dict objectForKey:@"F"]);

Replacing Objects

When you set a new object for the same key, Cocoa replaces the original object in the dictionary. This code replaces “3” with “foo” for the key “C”.

[dict setObject:@"foo" forKey:@"C"];
NSLog(@"%@", [dict objectForKey:@"C"]);

Removing Objects

You can also remove objects from dictionaries. This snippet removes the object associated with the “B” key. Once removed, both the key and the object no longer appear in the dictionary.

[dict removeObjectForKey:@"B"];

Listing Keys

Dictionaries can report the number of entries they store plus they can provide an array of all the keys currently in use. This key list lets you know what keys have already been used. It lets you test against the list before adding an item to the dictionary, avoiding overwriting an existing key/object pair.

NSLog(@"The dictionary has %d objects", [dict count]);
NSLog(@"%@", [dict allKeys]);

Accessing Set Objects

Sets store unordered collections of objects. You encounter sets almost exclusively when working with the iPhone’s multitouch screen. The UIView class receives finger movement updates that deliver touches as an NSSet. To work with touches, you almost always issue allObjects and work with the array that gets returned. Once converted, use standard array calls to list, query, and iterate through the touches.

Memory Management with Collections

Arrays, sets and dictionaries automatically retain objects when they are added and release those objects when they are removed from the collection. Releases are also sent when the collection is deallocated. Collections do not copy objects. Instead, they rely on retain counts to hold onto objects and use them as needed.

Writing Out Collections to File

Both arrays and dictionaries can store themselves into files using writeToFile: atomically: methods so long as the types within the collections belong to the set of NSData, NSDate, NSNumber, NSString, NSArray, and NSDictionary. Pass the path as the first argument, and a Boolean as the second. As when saving strings, the second argument determines whether the file is first stored to a temporary auxiliary and then renamed. The method returns a Boolean value: YES if the file was saved, NO if not. Storing arrays and dictionaries create standard property lists files.

image

To recover an array or dictionary from file, use the convenience methods arrayWithContentsOfFile: and dictionaryWithContentsOfFile:. If the methods return nil, the file could not be read.

NSArray *newArray = [NSArray arrayWithContentsOfFile:path];
NSLog(@"%@", newArray);

Building URLs

NSURL objects point to resources. These resources can refer to both local files and to URLs on the Web. Create url objects by passing a string to class convenience functions. Separate functions have been set up to interpret each kind of URL. Once built, however, NSURL objects are interchangeable. Cocoa does not care if the resource is local or points to an object only available via the Net. This code demonstrates building URLs of each type, path, and Web.

image

Working with NSData

If NSString objects are analogous to zero-terminated C strings, then NSData objects correspond to buffers. NSData provides data objects that store and manage bytes. Often, you fill NSData with the contents of a file or URL. The data returned can report its length, letting you know how many bytes were retrieved. This snippet retrieves the contents of a URL and prints the number of bytes that were read.

NSData *data = [NSData dataWithContentsOfURL:url2];
NSLog(@"%d", [data length]);

To access the core byte buffer that underlies an NSData object, use bytes. This returns a (const void *) pointer to actual data.

As with many other Cocoa objects, you can use the standard NSData version of the class or its mutable child, NSMutableData. Most Cocoa programs that access the Web, particularly those that perform asynchronous downloads, pull in a bit of data at a time. For those cases, NSMutableData objects prove useful. You can keep growing mutable data by issuing appendData: to add the new information as it is received.

File Management

The iPhone’s file manager is a singleton provided by the NSFileManager class. It can list the contents of folders to determine what files are found and perform basic file system tasks. The following snippet retrieves a file list from two folders. First it looks in the sandbox’s Documents folder and then inside the application bundle itself.

image

Note the use here of NSBundle. It lets you find the application bundle and pass its path to the file manager. You can also use NSBundle to retrieve the path for any item included in your app bundle. (You cannot, however, write to the application bundle at any time.) This code returns the path to the application’s Default.png image. Note that the file and extension names are separated and that each is case sensitive.

NSBundle *mb = [NSBundle mainBundle];
NSLog(@"%@", [mb pathForResource:@"Default" ofType:@"png"]);

The file manager offers a full suite of file-specific management. It can move, copy, and remove files as well as query the system for file traits and ownership. Here are some examples of the simpler routines you may use in your applications.

image

Note

As another convenient file trick, use tildes in path names, for example, “~/Library/Preferences/foo.plist” and apply the NSString method stringByExpandingTildeInPath.

One More Thing: Message Forwarding

Although Objective-C does not provide true multiple-inheritance, it offers a work-around that lets objects respond to messages that are implemented in other classes. If you want your object to respond to another class’s messages, you can add message forwarding to your applications and gain access to that object’s methods.

Normally, sending an unrecognized message produces a runtime error, causing an application to crash. But before the crash happens, the iPhone’s runtime system gives each object a second chance to handle a message. Catching that message lets you redirect it to an object that understands and can respond to that message.

Consider the Car example used throughout this chapter. The carInfo property introduced midway through these examples returns a string that describes the car’s make, model, and year. Now imagine if a Car instance could respond to NSString messages by passing them to that property. Send length to a Car object and instead of crashing, the object would return the length of the carInfo string. Send stringByAppendingString: and the object adds that string to the property string. It would be as if the Car class inherited (or at least borrowed) the complete suite of string behavior.

Objective-C provides this functionality through a process called message forwarding. When you send a message to an object that cannot handle that selector, the selector gets forwarded to a forwardInvocation: method. The object sent with this message, namely an NSInvocation instance stores the original selector and arguments that were requested. You can override forwardInvocation: and send that message on to another object.

Implementing Message Forwarding

To add message forwarding to your program, you must override two methods, namely, methodSignatureForSelector: and forwardInvocation:. The former creates a valid method signature for messages implemented by another class. The latter forwards the selector to an object that actually implements that message.

Building a Method Signature

This first method returns a method signature for the requested selector. For this example, a Car instance cannot properly create a signature for a selector implemented by another class, in this case NSString. Adding a check for a malformed signature (i.e., returning nil) gives this method the opportunity to iterate through each pseudo-inheritance and attempt to build a valid result. This example draws methods from just one other class via self.carInfo.

image

Forwarding

The second method you need to override is forwardInvocation:. This method only gets called when an object has been unable to handle a message. This method gives the object a second chance, allowing it to redirect that message. The method checks to see whether the self.carInfo string responds to the selector. If it does respond, it tells the invocation to invoke itself using that object as its receiver.

image

Using Forwarded Messages

Calling nonclass messages like UTF8String and length produces compile-time warnings, which you can ignore. The code shown in Figure 3-4 causes two compiler warnings. The code, however, compiles and (more importantly) runs without error. As the figure shows, you can send a Car instance methods that are defined by the class itself and also those implemented by NSString.

Figure 3-4 The compiler issues warnings for forwarded methods, but the code runs without error.

image

House Cleaning

Although invocation forwarding mimics multiple inheritance, NSObject never confuses the two. Methods like respondsToSelector: and isKindOfClass: only look at the inheritance hierarchy and not at the forwarding change.

A couple of optional methods allow your class to better express its message compliance to other classes. Reimplementing respondsToSelector: and isKindOfClass: lets other classes query your class. In return, the class announces that it responds to all string methods (in addition to its own) and that it is a “kind of” string, further emphasizing the pseudo-multiple inheritance approach.

image

Supereasy Forwarding

The method signature/forward invocation pair of methods provides a robust and approved way to add forwarding to your classes. A simpler approach is also available on the iPhone, which you can use at your own risk. You can replace both those methods with this single one, which does all the same work with less coding.

image

Summary

This chapter provided an abridged, high-octane introduction to Objective-C and Foundation. In it, you read about the way that Objective-C extends C and provides support for object-oriented programming. You discovered properties and memory management and were subjected to a speedy review of the most important Foundation classes. So what can you take away from this chapter? Here are a few final thoughts.

• The sample code for this chapter contains all the examples used throughout this introduction. Try testing this material directly in Xcode. Mess around with the material, add your own samples, or expand the ones you’ve been given. Hands-on offers the best way to gain critical skills you need for iPhone development.

• Learning Objective-C and Cocoa takes more than just a chapter. If you’re serious about learning iPhone programming, and these concepts are new to you, consider seeking out single-topic books that are dedicated to introducing these technologies to developers new to the platform. Consider Aaron Hillegass’s Cocoa Programming for Mac OS X, 3rd Edition, or Stephen Kochan’s Programming in Objective-C 2.0, 2nd Edition, or Fritz Anderson’s Xcode 3 Unleashed.

• This chapter mentioned Core Foundation and Carbon but did not delve into these technologies in any depth. You can and will experience C-based APIs on the iPhone, particularly when you work with the address book, with Quartz 2-D graphics, and with Core Audio, among other frameworks. Each of these specific topic areas are documented exhaustively at Apple’s developer Web site, complete with sample code. A strong grounding in C (and sometimes C++) programming will help you work through the specific implementation details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.183.152