Cocoa Classes - Cocoa - iOS 7 Programming Fundamentals: Objective-C, Xcode, and Cocoa Basics (2014)

iOS 7 Programming Fundamentals: Objective-C, Xcode, and Cocoa Basics (2014)

Part III. Cocoa

When you program for iOS, you take advantage of a suite of frameworks provided by Apple. These frameworks, taken together, constitute Cocoa; the brand of Cocoa that provides the API for programming iOS is Cocoa Touch. Cocoa thus plays an important and fundamental role in iOS programming; your code will ultimately be almost entirely about communicating with Cocoa. The Cocoa Touch frameworks provide the underlying functionality that any iOS app needs to have. Your app can put up a window, show the interface containing a button, respond to that button being tapped by the user, and so forth, because Cocoa knows how to do those things. But with the great advantages of working with a framework come great responsibilities. You have to think the way the framework thinks, put your code where the framework expects it, and fulfill many obligations imposed on you by the framework.

§ Chapter 10 picks up where Chapter 5 left off, describing some Objective-C linguistic features used by Cocoa, such as categories and protocols; it also surveys some important fundamental classes.

§ Chapter 11 describes Cocoa’s event-driven model, along with its major design patterns. An event is a message sent by Cocoa to your code. Cocoa is event-based; if Cocoa doesn’t send your code an event, your code doesn’t run. Getting your code to run at the appropriate moment is all about knowing what events you can expect Cocoa to send you and when.

§ Chapter 12 describes your responsibilities for making your instances nicely encapsulated and good memory-management citizens in the world of Cocoa objects.

§ Chapter 13 surveys some answers to the question of how your objects are going to see and communicate with one another within the Cocoa-based world.

Chapter 10. Cocoa Classes

Using the Cocoa Touch frameworks requires an understanding of how those frameworks organize their classes. Cocoa class organization depends upon certain Objective-C language features that are introduced in this chapter. The chapter also surveys some commonly used Cocoa utility classes, along with a discussion of the Cocoa root class.

Subclassing

Cocoa effectively hands you a large repertory of objects that already know how to behave in certain desirable ways. A UIButton, for example, knows how to draw itself and how to respond when the user taps it; a UITextField knows how to display editable text, how to summon the keyboard, and how to accept keyboard input.

Often, the default behavior or appearance of an object supplied by Cocoa won’t be quite what you’re after, and you’ll want to customize it. This does not necessarily mean that you need to subclass! Cocoa classes are heavily endowed with methods that you can call, and properties that you can set, precisely in order to customize an instance, and these will be your first resort. Always study the documentation for a Cocoa class to see whether instances can already be made to do what you want. For example, the class documentation for UIButton shows that you can set a button’s title, title color, internal image, background image, and many other features and behaviors, without subclassing.

Nevertheless, sometimes setting properties and calling methods won’t suffice to customize an instance the way you want to. In such cases, Cocoa may provide methods that are called internally as an instance does its thing, and whose behavior you can customize by subclassing and overriding (Chapter 4). You don’t have the code to any of Cocoa’s built-in classes, but you can still subclass them, creating a new class that acts just like a built-in class except for the modifications you provide.

Oddly enough (and you might be particularly surprised by this if you’ve used another object-oriented application framework), subclassing is probably one of the less important ways in which your code will relate to Cocoa. Knowing when to subclass can be somewhat tricky, but the general rule is that you probably shouldn’t subclass unless you’re invited to. Some Cocoa Touch classes are subclassed very commonly; for example, a plain vanilla UIViewController, not subclassed, is very rare. But most built-in Cocoa Touch classes will never need subclassing (and some, in their documentation, downright forbid it).

Take, for instance, the case of UIView. Cocoa Touch is full of built-in UIView subclasses that behave and draw themselves as needed (UIButton, UITextField, and so on), and you will rarely need to subclass any of them. On the other hand, you might create your own UIView subclass, whose job would be to draw itself in some completely new way. You don’t actually draw a UIView; rather, when a UIView needs drawing, its drawRect: method is called so that the view can draw itself. So the way to draw a UIView in some completely custom manner is to subclass UIView and implement drawRect: in the subclass. As the documentation says, “Implement this method if your view draws custom content.” The word “implement” can only mean “implement in a subclass” — that is, subclass and override. The documentation is saying that you need to subclass UIView in order to draw content that is completely your own.

For example, suppose we want our window to contain a horizontal line. There is no horizontal line interface widget built into Cocoa, so we’ll just have to roll our own — a UIView that draws itself as a horizontal line. Let’s try it:

1. In our Empty Window example project, choose File → New → File and specify a Cocoa Touch Objective-C class, and in particular a subclass of UIView. Call the class MyHorizLine. Xcode creates MyHorizLine.m and MyHorizLine.h. Make sure, when creating them, that you make them part of the app target.

2. In MyHorizLine.m, replace the contents of the implementation section with this (without further explanation):

3. - (id)initWithCoder:(NSCoder *)decoder {

4. self = [super initWithCoder:decoder];

5. if (self) {

6. self.backgroundColor = [UIColor clearColor];

7. }

8. return self;

9. }

10.

11.- (void)drawRect:(CGRect)rect {

12. CGContextRef c = UIGraphicsGetCurrentContext();

13. CGContextMoveToPoint(c, 0, 0);

14. CGContextAddLineToPoint(c, self.bounds.size.width, 0);

15. CGContextStrokePath(c);

}

16.Edit the storyboard. Find UIView in the Object library (it is called simply “View”), and drag it into the View object in the canvas. You may resize it to be less tall.

17.With the UIView that you just dragged into the canvas still selected, use the Identity inspector to change its class to MyHorizLine.

Build and run the app in the Simulator. You’ll see a horizontal line corresponding to the location of the top of the MyHorizLine instance in the nib. Our view has drawn itself as a horizontal line, because we subclassed it to do so.

In that example, we started with a bare UIView that had no drawing functionality of its own. That’s why there was no need to call super; the default implementation of UIView’s drawRect: does nothing. But you might also be able to subclass a built-in UIView subclass to modify the way it already draws itself. For example, the UILabel documentation shows that two methods are present for exactly this purpose. Both drawTextInRect: and textRectForBounds:limitedToNumberOfLines: explicitly tell us: “You should not call this method directly. This method should only be overridden by subclasses.” The implication is that these are methods that will be called for us, automatically, by Cocoa, as a label draws itself; thus, we can subclass UILabel and implement these methods in our subclass to modify how a particular type of label draws itself.

Here’s an example from one of my own apps, in which I subclass UILabel to make a label that draws its own rectangular border and has its content inset somewhat from that border, by overriding drawTextInRect:. As the documentation tells us: “In your overridden method, you can configure the current [graphics] context further and then invoke super to do the actual drawing [of the text].” Let’s try it:

1. In the Empty Window project, make a new class file, a UILabel subclass; call the class MyBoundedLabel.

2. In MyBoundedLabel.m, insert this code into the implementation section:

3. - (void)drawTextInRect:(CGRect)rect {

4. CGContextRef context = UIGraphicsGetCurrentContext();

5. CGContextStrokeRect(context, CGRectInset(self.bounds, 1.0, 1.0));

6. [super drawTextInRect:CGRectInset(rect, 5.0, 5.0)];

}

7. Edit the storyboard, add a UILabel to the interface, and change its class in the Identity inspector to MyBoundedLabel.

Build and run the app, and you’ll see how the rectangle is drawn and the label’s text is inset within it. (It may help to add an identical UILabel to the interface, whose class is UILabel, so as to observe the difference in how they draw themselves.)

Another reason why subclassing is rare in Cocoa is that so many built-in classes use the delegate mechanism (Chapter 11) as a way of letting you customize their behavior. You wouldn’t subclass UIApplication (the class of the singleton shared application instance) just in order to respond when the application has finished launching, because the delegate mechanism provides a way to do that (application:didFinishLaunchingWithOptions:). That’s why the templates give us an AppDelegate class, which is not a UIApplication subclass. On the other hand, if you needed to perform certain tricky customizations of your app’s fundamental event messaging behavior, you might subclass UIApplication in order to override sendEvent:. The documentation does tell you this, and it also tells you, rightly, that doing so would be very rare.

NOTE

If you do subclass UIApplication, you’ll want to change the third argument in the call to UIApplicationMain in main.m from nil to the NSString name of your subclass. Otherwise your UIApplication subclass won’t be instantiated as the shared application instance. See Chapter 6.

Categories

A category is an Objective-C language feature that allows you to reach right into an existing class and inject additional methods. You can do this even if you don’t have the code for the class, as with Cocoa’s classes. Your instance methods can refer to self, and this will mean the instance to which the message was originally sent, as usual. A category, unlike a subclass, cannot define additional instance variables; it can override methods, but you should probably not take advantage of this ability.

Defining a category is almost exactly like defining a class (Chapter 4). You’ll need an interface section and an implementation section, and you’ll typically distribute them into the standard .h and .m class file pair. But:

§ There’s no need, in the interface section, to declare a superclass; this class already exists, and its superclass has already been declared. Indeed, the interface section must have access to the declaration for the original class, typically by importing the header for the original class (or the header of the framework that defines it).

§ At the start of both the interface section and the implementation section, where you give the class’s name, you add a category name in parentheses.

If you’re using a .h and .m file pair, the .m file will, as usual, import the .h file. Also, if any other .m file in your project needs to call any of your category’s injected methods, those methods must be made public with a declaration in the category’s .h file, and that .m file must import the category’s .h file.

The easiest way to set up a .h and .m category file pair is to ask Xcode to do it for you. Choose File → New → File, and in the “Choose a template” dialog, among the iOS Cocoa Touch file types, pick “Objective-C category”. You’ll be asked to give a name for the category and the class on which you’re defining this category.

On the other hand, if only one of your classes needs to call this category’s injected methods, it is perfectly reasonable to put both the category’s interface section and the category’s implementation section into that class’s .m file. In that case, the category’s interface section can be empty; the .mfile can see and use the methods defined in the category’s implementation section.

For example, in one of my apps I found myself performing a bunch of string transformations in order to derive the path to various resource files inside the app bundle based on the resource’s name and purpose. I ended up with half a dozen utility methods. Given that these methods all operated on an NSString, it was appropriate to implement them as a category of NSString, thus allowing any NSString, anywhere in my code, to respond to them.

The code was structured like this (I’ll show just one of the methods):

// StringCategories.h:

#import <Foundation/Foundation.h>

@interface NSString (MyStringCategories)

- (NSString*) basePictureName;

@end

// StringCategories.m:

#import "StringCategories.h"

@implementation NSString (MyStringCategories)

- (NSString*) basePictureName {

return [self stringByAppendingString:@"IO"];

}

@end

If basePictureName had been implemented as a utility method within some other class, it would need to take a parameter — we’d have to pass an NSString to it — and, if it were an instance method, we’d need to go to the extra trouble of obtaining a reference to an instance of that class. A category is neater and more compact. We’ve extended NSString itself to have basePictureName as an instance method, so, in any .m file that imports StringCategories.h, we can send the basePictureName message directly to any NSString we want to transform:

NSString* aName = [someString basePictureName];

A category is particularly appropriate in the case of a class like NSString, because the documentation warns us that subclassing NSString is a bad idea. That’s because NSString is part of a complex of classes called a class cluster, which means that an NSString object’s real class might actually be some other class. A category is a much better way to modify a class within a class cluster than subclassing.

A method defined through a category can equally be a class method. Thus you can inject utility methods into any appropriate class and call those methods without the overhead of instantiating anything at all. Classes are globally available, so your method becomes, in effect, a global method (see Chapter 13).

For example, in one of my apps I found myself frequently using a certain color (UIColor). Rather than repeating the instructions for generating that color every time I wanted to use it, I put those instructions in one place — a category’s implementation section, in a .m file:

@implementation UIColor (MyColors)

+ (UIColor*) myGolden {

return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900];

}

@end

I declared myGolden in the category’s interface section in the corresponding .h file, and imported that .h file in my project’s .pch file (the precompiled header). Since the precompiled header is automatically imported throughout my project, I could now call [UIColor myGolden] anywhere.

Splitting a Class

A category can be used to split a class over multiple .h/.m file pairs. If a class threatens to become long and unwieldy, yet it clearly needs to be a single class, you can define the basic part of it (including instance variables) in one file pair, and then add another file pair defining a category on your own class to contain further methods.

Cocoa itself does this. A good example is NSString. NSString is defined as part of the Foundation framework, and its basic methods are declared in NSString.h. Here we find that NSString itself, with no category, has just two methods, length and characterAtIndex:, because these are regarded as the minimum that a string needs to do in order to be a string. Additional methods — those that create a string, deal with a string’s encoding, split a string, search in a string, and so on — are clumped into categories. The interface for some of these categories appears in this same file, NSString.h. But a string may serve as a file pathname, so we also find a category on NSString in NSPathUtilities.h, where methods are declared for splitting a pathname string into its constituents and the like. Then, in NSURL.h, there’s another NSString category, declaring methods for dealing with percent-escaping in a URL string. Finally, off in a completely different framework (UIKit), NSStringDrawing.h adds two further NSString categories, with methods about drawing a string in a graphics context.

This organization won’t matter to you as a programmer, because an NSString is an NSString, no matter how it acquires its methods, but it can matter when you consult the documentation. The NSString methods declared in NSString.h, NSPathUtilities.h, and NSURL.h are documented in the NSString class documentation page, but the NSString methods declared in NSStringDrawing.h are not, presumably because they originate in a different framework. Instead, they appear in a separate document, NSString UIKit Additions Reference. As a result, the string drawing methods can be difficult to discover, especially as the NSString class documentation doesn’t link to the other document. I regard this as a major flaw in the structure of the Cocoa documentation. A third-party utility such as AppKiDo can be helpful here.

Class Extensions

A class extension is a nameless category that exists solely as an interface section, like this:

@interface MyClass ()

// stuff goes here

@end

Typically, the only classes that will be permitted to “see” a class extension will be the class that’s being extended or a subclass of that class. If only the former is the case, the class extension will usually appear directly in the class’s implementation (.m) file, like this:

// MyClass.m:

@interface MyClass ()

// stuff goes here

@end

@implementation MyClass {

// ivars

}

// methods

@end

That’s such a common arrangement that Xcode’s project template files actually give you a class extension in certain classes. For example, our Empty Window project comes with a class extension at the start of ViewController.m — take a look and see!

What on earth sort of “stuff” could possibly go into a class extension to make it so useful that it appears in a template file? First, I’ll tell you what a class extension used to be used for: it was the standard solution to the problem of method definition order.

Before the Objective-C language improvements introduced by LLVM compiler version 3.1 (Xcode 4.3), one method in an implementation section couldn’t call another method in that same implementation section unless either the definition or a method declaration for that other method preceded it. It’s finicky work trying to arrange all the method definitions in the right order, so the obvious solution is a method declaration. A method declaration can go only into an interface section. But to put a method declaration into the interface section in this class’s header file is annoying — it means we must switch to another file — and, even worse, it makes that method public; any class that imports that header file can now see and call this method. That’s fine if this method is supposed to be public; but what if we wanted to keep it private? The solution: a class extension at the start of this class’s implementation file. Put the method declarations into that class extension; all the methods in the implementation section can now see those method declarations and can call one another, but no other class can see them.

Nowadays, though, that trick is unnecessary: methods (and functions) in a class implementation can see and call one another regardless of order.

In modern Objective-C, the usefulness of class extensions has to do mostly with property declarations (which I don’t discuss until Chapter 12). Property declarations, like method declarations, must appear in an interface section; and, although some properties are intended to be public, it is also often the case that we would prefer certain properties to remain private to the class — they are global to all methods of the class, and are useful for preserving values and permitting those values to be accessed by multiple methods, but no other class can see them. The solution is to declare the private properties in a class extension.

(For example, in Chapter 7 we declared an IBOutlet property in a class’s .h file. But in real life we are much more likely to declare such a property in a class extension of the .m file; a class’s outlets are usually no other class’s business.)

I’ll describe yet another use of class extensions in the next section.

Protocols

Every reasonably sophisticated object-oriented language must face the fact that the hierarchy of subclasses and superclasses is insufficient to express the desired relationships between classes. For example, a Bee object and a Bird object might need to have certain features in common by virtue of the fact that both a bee and a bird can fly. But Bee might inherit from Insect, and not every insect can fly, so how can Bee acquire the aspects of a Flier in a way that isn’t completely independent of how Bird acquires them?

Some object-oriented languages solve this problem through mixin classes. For example, in Ruby you could define a Flier module, complete with method definitions, and incorporate it into both Bee and Bird. Objective-C uses a simpler, lighter-weight approach — the protocol. Cocoa makes heavy use of protocols.

A protocol is just a named list of method declarations, with no implementation. A class may formally declare that it conforms to (or adopts) a protocol; such conformance is inherited by subclasses. This declaration satisfies the compiler when you try to send a corresponding message: if a protocol declares an instance method myCoolMethod, and if MyClass declares conformance to that protocol, then you can send the myCoolMethod message to a MyClass instance and the compiler won’t complain.

Actually implementing the methods declared in a protocol is up to the class that conforms to it. A protocol method may be required or optional. If a protocol method is required, then if a class conforms to that protocol, the compiler will warn if that class fails to implement that method. Implementing optional methods, on the other hand, is optional. (Of course, that’s just the compiler’s point of view; at runtime, if a message is sent to an object with no implementation for the corresponding method, a crash can result; see Chapter 3.)

Here’s an example of how Cocoa uses a protocol. Some objects can be copied; some can’t. This has nothing to do with an object’s class heritage. Yet we would like a uniform method to which any object that can be copied will respond. So Cocoa defines a protocol named NSCopying, which declares just one method, copyWithZone: (required). A class that explicitly conforms to NSCopying is promising that it implements copyWithZone:.

Here’s how the NSCopying protocol is defined (in NSObject.h, where your code can see it):

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end

That’s all there is to defining a protocol. The definition uses the @protocol compiler directive; it states the name of the protocol; it consists entirely of method declarations; and it is terminated by the @end compiler directive.

A protocol definition will typically appear in a header file, so that classes that need to know about it, in order to adopt it or call its methods, can import it. A protocol section of a header file is not inside any other section (such as an interface section).

Any optional methods must be preceded by the @optional directive. A protocol definition may state that the protocol incorporates other protocols; these constitute a comma-separated list in angle brackets after the protocol’s name, like this example from Apple’s own code (UIAlertView.h):

@protocol UIAlertViewDelegate <NSObject>

@optional

- (void)alertView:(UIAlertView *)alertView

clickedButtonAtIndex:(NSInteger)buttonIndex;

// ... more optional method declarations ...

@end

The NSCopying protocol definition in NSObject.h is just a definition; it is not a statement that NSObject conforms to NSCopying. Indeed, NSObject does not conform to NSCopying! To see this, try sending the copyWithZone: method to your own subclass of NSObject:

MyClass* mc = [MyClass new];

MyClass* mc2 = [mc copyWithZone: nil];

Under ARC, that code won’t compile, because no implementation of copyWithZone: has been inherited.

To conform formally to a protocol, a class’s interface section appends the name of the protocol, in angle brackets, after the name of the superclass (or, if this is a category declaration, after the parentheses). This will necessitate importing the header file that declares the protocol (or some header file that imports that header file). To state that a class conforms to multiple protocols, put multiple protocol names in the angle brackets, separated by comma.

Let’s see what happens if you conform formally to the NSCopying protocol. Modify the first line of the interface section of your class as follows:

@interface MyClass : NSObject <NSCopying>

Now your code compiles, but the compiler warns that MyClass fails to implement copyWithZone:, which it is contracted to do because copyWithZone: is a required method of the NSCopying protocol.

The name of a protocol may also be used when specifying an object type. Most often, the object will be typed as an id, but with the accompanying proviso that it conforms to a protocol, whose name appears in angle brackets.

To illustrate, let’s look at another typical example of how Cocoa uses protocols, namely in connection with a table view (UITableView). A UITableView has a dataSource property, declared like this:

@property (nonatomic, assign) id<UITableViewDataSource> dataSource

This property represents an instance variable whose type is id <UITableViewDataSource>. This means “I don’t care what class my data source belongs to, but whatever it is, it should conform to the UITableViewDataSource protocol.” Such conformance constitutes a promise that the data source will implement at least the required instance methods tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:, which the table view will call when it needs to know what data to display.

If you attempt to set a table view’s dataSource property to an object that does not conform to UITableViewDataSource, you’ll get a warning from the compiler. So, for example:

MyClass* mc = [MyClass new];

UITableView* tv = [UITableView new];

tv.dataSource = mc; // compiler warns

Under ARC, this warning is couched in rather confusing terms, along these lines: “Assigning to ‘id<UITableViewDataSource>’ from incompatible type ‘MyClass *__strong’.”

To quiet the compiler, MyClass’s declaration should state that it conforms to UITableViewDataSource. Once it does so, MyClass is an id <UITableViewDataSource>, and the third line no longer generates a warning. Of course, you must also supply implementations oftableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: in MyClass to avoid the other warning, namely that you’re not implementing a required method of a protocol you’ve claimed to conform to.

In a very large percentage of cases, the object that you want to assign where conformity to a protocol is expected is self. In this situation, you can declare this class’s conformity to the protocol in the implementation (.m) file as part of a class extension, like this:

// MyClass.m:

@interface MyClass () <UITableViewDataSource>

@end

@implementation MyClass

- (void) someMethod {

UITableView* tv = [UITableView new];

tv.dataSource = self;

}

@end

I prefer this arrangement, because it means that the declaration of conformity to the protocol is right there in the same file that uses the protocol.

A prevalent use of protocols in Cocoa is in connection with delegate objects (and indeed, it is primarily in order to implement delegation that you are most likely to define your own protocols). We’ll talk in detail about delegates in Chapter 11, but you can readily see that many classes have adelegate property and that the class of this property is often id <SomeProtocol>. For example, in our Empty Window project, the AppDelegate class provided by the project template is declared like this:

@interface AppDelegate : UIResponder <UIApplicationDelegate>

The reason is that AppDelegate’s purpose on earth is to serve as the shared application’s delegate. The shared application object is a UIApplication, and UIApplication’s delegate property is typed as an id <UIApplicationDelegate>. So AppDelegate announces its role by explicitly conforming to UIApplicationDelegate.

A slight chicken-and-egg problem arises in a header file containing a protocol definition and a class’s interface section, each of which mentions the other. The class’s interface section, it seems, cannot come first, because it mentions the protocol before the protocol has been defined; but the protocol definition can’t come first either, because it mentions the class before the class has been defined. The usual solution is to put the class’s interface section first, preceded by a forward declaration of the protocol, a single line introducing just the name of the protocol, whose definition will be forthcoming elsewhere — meaning, as it turns out, three lines from now:

@protocol MyProtocol;

@interface MyClass : NSObject

@property (nonatomic, weak) id<MyProtocol> delegate;

@end

@protocol MyProtocol

- (void) doSomething: (MyClass*) m;

@end

As a programmer, Cocoa’s use of protocols will matter to you in two ways:

Conformity

If an object value that you wish to assign or pass as an argument is typed as id <SomeProtocol>, you must make sure that that object’s class does indeed conform to SomeProtocol (and implements any methods required by that protocol).

Using the documentation

A protocol has its own documentation page. When the UIApplication class documentation tells you that the delegate property is typed as an id <UIApplicationDelegate>, it’s implicitly telling you that if you want to know what messages a UIApplication’s delegate might receive, you need to look in the UIApplicationDelegate protocol documentation.

Similarly, when a class’s documentation mentions that the class conforms to a protocol, don’t forget to examine that protocol’s documentation, because the latter might contain important information about how the class behaves. To learn what messages can be sent to an object, as I already mentioned in Chapter 8, you need to look upward through the superclass inheritance chain; you also need to look at any protocols that this object’s class (or superclass) conforms to.

Informal Protocols

You may occasionally see, online or in the documentation, a reference to an informal protocol. An informal protocol isn’t really a protocol at all; it’s just a way of providing the compiler with a method signature so that it will allow a message to be sent without complaining.

There are two complementary ways to implement an informal protocol. One is to define a category on NSObject; this makes any object eligible to receive the messages listed in the category. The other is to define a protocol to which no class formally conforms; instead, send any message listed in the protocol only to objects typed as id, thus suppressing any possible objections from the compiler.

These techniques were widespread before protocols could declare methods as optional; now they are largely unnecessary. (They are still used, but decreasingly so; in iOS 7 very few informal protocols remain.) They are also mildly dangerous, because you might accidentally define a method with the same name as an existing method but a different signature, with unpredictable results.

Optional Methods

A protocol can explicitly designate some or all of its methods as optional. The question thus arises: How, in practice, is such an optional method feasible? We know that if a message is sent to an object and the object can’t handle that message, an exception is raised and your app will likely crash. But a method declaration is a contract suggesting that the object can handle that message. If we subvert that contract by declaring a method that might or might not be implemented, aren’t we inviting crashes?

The answer is that Objective-C is not only dynamic but also introspective. You can ask an object whether it can really deal with a message without actually sending it that message. The key method here is NSObject’s respondsToSelector:, which takes a selector parameter and returns a BOOL. With it, you can send a message to an object only if it would be safe to do so:

MyClass* mc = [MyClass new];

if ([mc respondsToSelector:@selector(woohoo)]) {

[mc woohoo];

}

You wouldn’t want to do this before sending just any old message, because it isn’t necessary except for optional methods, and it slows things down a little. But Cocoa does in fact call respondsToSelector: on your objects as a matter of course. To see that this is true, implementrespondsToSelector: on AppDelegate in our Empty Window project and instrument it with logging:

- (BOOL) respondsToSelector: (SEL) sel {

NSLog(@"%@", NSStringFromSelector(sel));

return [super respondsToSelector:(sel)];

}

The output on my machine, as the Empty Window app launches, includes the following (I’m omitting private methods and multiple calls to the same method):

application:handleOpenURL:

application:openURL:sourceApplication:annotation:

applicationDidReceiveMemoryWarning:

applicationWillTerminate:

applicationSignificantTimeChange:

application:willChangeStatusBarOrientation:duration:

application:didChangeStatusBarOrientation:

application:willChangeStatusBarFrame:

application:didChangeStatusBarFrame:

application:deviceAccelerated:

application:deviceChangedOrientation:

applicationDidBecomeActive:

applicationWillResignActive:

applicationDidEnterBackground:

applicationWillEnterForeground:

applicationWillSuspend:

application:didResumeWithOptions:

application:shouldSaveApplicationState:

application:supportedInterfaceOrientationsForWindow:

application:performFetchWithCompletionHandler:

application:didReceiveRemoteNotification:fetchCompletionHandler:

application:willFinishLaunchingWithOptions:

application:didFinishLaunchingWithOptions:

That’s Cocoa, checking to see which of the optional UIApplicationDelegate protocol methods (including a couple of undocumented methods) are actually implemented by our AppDelegate instance — which, because it is the UIApplication object’s delegate and formally conforms to the UIApplicationDelegate protocol, has explicitly agreed that it might be willing to respond to any of those messages. The entire delegate pattern (Chapter 11) depends upon this technique. Observe the policy followed here by Cocoa: it checks all the optional protocol methods once, when it first meets the object in question, and presumably stores the results; thus, the app is slowed a tiny bit by this one-time initial bombardment of respondsToSelector: calls, but now Cocoa knows all the answers and won’t have to perform any of these same checks on the same object later.

Some Foundation Classes

The Foundation classes of Cocoa provide basic data types and utilities that will form the basis of much that you do in Cocoa. Obviously I can’t list all of them, let alone describe them fully, but I can survey a few that I use frequently and that you’ll probably want to be aware of before writing even the simplest Cocoa program. For more information, start with Apple’s list of the Foundation classes in the Foundation Framework Reference.

Useful Structs and Constants

NSRange is a struct of importance in dealing with some of the classes I’m about to discuss. Its components are integers (NSUInteger), location and length. For example, a range whose location is 1 starts at the second element of something (because element counting is always zero-based), and if its length is 2 it designates this element and the next. Cocoa also supplies various convenience methods for dealing with a range; you’ll use NSMakeRange frequently. (Note that the name, NSMakeRange, is backward compared to names like CGPointMake and CGRectMake.)

NSNotFound is a constant integer indicating that some requested element was not found. For example, if you ask for the index of a certain object in an NSArray and the object isn’t present in the array, the result is NSNotFound. (The result could not be 0 to indicate the absence of the object, because 0 would indicate the first element of the array. Nor could it be nil, because nil is 0, and in any case is not appropriate when an integer is expected. Nor could it be -1, because an array index value is always positive.) The true numeric value of NSNotFound is of no concern to you; always compare against NSNotFound itself, to learn whether a result is a meaningful index.

If a search returns a range and the thing sought is not present, the location component of the resulting NSRange will be NSNotFound.

NSString and Friends

NSString, which has already been used rather liberally in examples in this book, is the Cocoa object version of a string. You can create an NSString through a number of class methods and initializers, or by using the NSString literal notation @"...", which is really a compiler directive. Particularly important is stringWithFormat:, which lets you convert numbers to strings and combine strings; see Chapter 9, where I discussed format strings in connection with NSLog. Here are some strings in action:

int x = 5;

NSString* s = @"widgets";

NSString* s2 = [NSString stringWithFormat:@"You have %d %@.", x, s];

NSString has a modern, Unicode-based idea of what a string can consist of. A string’s “elements” are its characters, whose count is its length. These are not bytes, because the numeric representation of a Unicode character could be multiple bytes, depending on the encoding. Nor are they glyphs, because a composed character sequence that prints as a single “letter” can consist of multiple characters. Thus the length of an NSRange indicating a single “character” might be greater than 1. See the “Characters and Grapheme Clusters” chapter of Apple’s String Programming Guide.

An NSString can be searched using various rangeOf... methods, which return an NSRange. In addition, NSScanner lets you walk through a string looking for pieces that fit certain criteria; for example, with NSScanner (and NSCharacterSet) you can skip past everything in a string that precedes a number and then extract the number. The rangeOfString: family of search methods lets you look for a substring; using the option NSRegularExpressionSearch, you can search using a regular expression, and regular expressions are also supported as a separate class,NSRegularExpression (which uses NSTextCheckingResult to describe match results).

In this example from one of my apps, the user has tapped a button whose title is something like “5 by 4” or “4 by 3”. I want to know both numbers; one tells me how many rows the layout is to have, the other how many columns. I use an NSScanner to locate the two numbers in the title:

NSString* s = // title of button, e.g. @"4 by 3"

NSScanner* sc = [NSScanner scannerWithString:s];

int rows, cols;

[sc scanInt:&rows];

[sc scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]

intoString:nil];

[sc scanInt:&cols];

After that, rows and cols hold the desired numbers. Here’s how I might do the same thing using a regular expression:

NSString* s = // title of button, e.g. @"4 by 3"

int rowcol[2]; int* prowcol = rowcol;

NSError* err = nil;

NSRegularExpression* r =

[NSRegularExpression regularExpressionWithPattern:@"\\d"

options:0

error:&err];

// error-checking omitted

for (NSTextCheckingResult* match in

[r matchesInString:s options:0 range:NSMakeRange(0, [s length])])

*prowcol++ = [[s substringWithRange: [match range]] intValue];

After that, rowcol[0] and rowcol[1] hold the desired numbers. The syntax seems oddly tortured, though, because we must convert each match from an NSTextCheckingResult to a range, then to a substring of our original string, and finally to an integer.

More sophisticated automated textual analysis is supported by some additional classes, such as NSDataDetector, an NSRegularExpression subclass that efficiently finds certain types of string expression such as a URL or a phone number, and NSLinguisticTagger, which actually attempts to analyze text into its grammatical parts of speech.

An NSString object’s string is immutable. You can use a string to generate another string in various ways, such as by appending another string or by extracting a substring, but you can’t alter the string itself. For that, you need NSString’s subclass, NSMutableString.

NSString has convenience utilities for working with a file path string, and is often used in conjunction with NSURL, which is another Foundation class worth looking into. NSString and some other classes discussed in this section provide methods for writing out to a file’s contents or reading in a file’s contents; when you call those methods, the file can be specified either as an NSString file path or as an NSURL.

An NSString carries no font and size information. Interface objects that display strings (such as UILabel) have a font property that is a UIFont; but this determines the single font and size in which the string will display. Before iOS 6, display of styled text — where different runs of text have different style attributes (size, font, color, and so forth) — was quite challenging. The NSAttributedString class, embodying a string along with style runs, required the use of Core Text, and you had to lay out the styled text by drawing it yourself; you couldn’t display styled text in any standard interface object. Starting in iOS 6, however, NSAttributedString became a full-fledged Objective-C class. It has methods and supporting classes that allow you to style text and paragraphs easily in sophisticated ways — and the built-in interface objects that display text can display styled text.

String drawing in a graphics context can be performed with methods provided through the NSStringDrawing category on NSString (see the String UIKit Additions Reference) and on NSAttributedString (see the NSAttributedString UIKit Additions Reference).

NSDate and Friends

An NSDate is a date and time, represented internally as a number of seconds (NSTimeInterval) since some reference date. Calling [NSDate new] or [NSDate date] gives you a date object for the current date and time; other date operations may involve NSDateComponents and NSCalendar and can be a bit tricky because calendars are complicated (see the Date and Time Programming Guide). Here’s an example of constructing a date based on its calendrical values:

NSCalendar* greg =

[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents* comp = [NSDateComponents new];

comp.year = 2013;

comp.month = 8;

comp.day = 10;

comp.hour = 15;

NSDate* d = [greg dateFromComponents:comp];

Similarly, the way to do correct date arithmetic is to use NSDateComponents. Here’s how to add one day to a given date:

NSDate* d = // whatever

NSDateComponents* comp = [NSDateComponents new];

comp.day = 1;

NSCalendar* greg =

[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDate* d2 = [greg dateByAddingComponents:comp toDate:d options:0];

You will also likely be concerned with dates represented as strings. Creation and parsing of date strings involves NSDateFormatter, which uses a format string similar to NSString’s stringWithFormat. A complication is added by the fact that the exact string representation of a date component or format can depend upon the user’s locale, consisting of language, region format, and calendar settings. (Actually, locale considerations can also play a role in NSString format strings.)

In this example from one of my apps, I prepare the content of a UILabel reporting the date and time when our data was last updated. The app is not localized — the word “at” appearing in the string is always going to be in English — so I want complete control of the presentation of the date and time components as well. To get it, I have to insist upon a particular locale:

NSDateFormatter *df = [NSDateFormatter new];

if ([[NSLocale availableLocaleIdentifiers] indexOfObject:@"en_US"]

!= NSNotFound) {

NSLocale* loc = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

[df setLocale:loc]; // English month name and time zone name if possible

}

[df setDateFormat:@"'Updated' d MMMM yyyy 'at' h:mm a z"];

NSString* updatedString = [df stringFromDate: [NSDate date]]; // just now

At the other end of the spectrum, to surrender completely to the user’s locale, generate an NSDateFormatter’s format with dateFormatFromTemplate:options:locale: and the current locale. The “template” is a string listing the date components to be used, but their order, punctuation, and language are left up to the locale:

NSDateFormatter *df = [NSDateFormatter new];

NSString* format =

[NSDateFormatter dateFormatFromTemplate:@"dMMMMyyyyhmmaz"

options:0 locale:[NSLocale currentLocale]];

[df setDateFormat:format];

NSString* updatedString = [df stringFromDate: [NSDate date]]; // just now

On a device set to a French locale (Settings → General → International → Region Format), the result might be “20 juillet 2013 5:14 PM UTC−7”. Observe that a locale is not a system language: that result is in French because of the device’s locale setting, not its language setting. To learn more about locales in general, consult in your browser the documentation for ICU (International Components for Unicode), from which the iOS support for creating and parsing date strings is derived. To study what locales exist, use the locale explorer at http://demo.icu-project.org/icu-bin/locexp.

An extremely common beginner error is forgetting that a date has a time zone. For example, when logging an NSDate with NSLog, the time (and day) may appear to be wrong, because the value is being represented as GMT (London). To prevent this, call descriptionWithLocale:, supplying the desired locale (usually the current locale), or use a date formatter.

NSNumber

An NSNumber is an object that wraps a numeric value (including BOOL). Thus, you can use it to store and pass a number where an object is expected. An NSNumber is formed from an actual number with a method that specifies the numeric type; for example, you can callnumberWithInt: to form a number from an int:

[[NSUserDefaults standardUserDefaults] registerDefaults:

[NSDictionary dictionaryWithObjectsAndKeys:

[NSNumber numberWithInt: 4],

@"cardMatrixRows",

[NSNumber numberWithInt: 3],

@"cardMatrixColumns",

nil]];

As I mentioned in Chapter 5, LLVM compiler version 4.0 (Xcode 4.4) brought with it a new syntax for forming a new NSNumber instance:

§ Precede a literal number (or BOOL) with @. To specify further the numeric type, follow the literal number with U (unsigned integer), L (long integer), LL (long long integer), or F (float). For example, @3.1415 is equivalent to [NSNumber numberWithDouble:3.1415]; @YES is equivalent to [NSNumber numberWithBool:YES].

§ If an expression yields a number, wrap it in parentheses and precede the left parenthesis with @. For example, if height and width are floats, @(height/width) is equivalent to [NSNumber numberWithFloat: height/width].

Thus, the preceding example can be rewritten like this:

[[NSUserDefaults standardUserDefaults] registerDefaults:

[NSDictionary dictionaryWithObjectsAndKeys:

@4,

@"cardMatrixRows",

@3,

@"cardMatrixColumns",

nil]];

(There is also an NSDictionary literal syntax, so it will turn out, a few pages from now, that we can rewrite that code even more compactly.)

An NSNumber is not itself a number, however, so you can’t use it in calculations or where an actual number is expected. Instead, you must explicitly extract the number from its NSNumber wrapper using the inverse of the method that wrapped the number to begin with. Knowing what that method was is up to you. So, for example, if an NSNumber wraps an int, you can call intValue to extract the int:

NSUserDefaults* ud = [NSUserDefaults standardUserDefaults];

int therows = [[ud objectForKey:@"cardMatrixRows"] intValue];

int thecols = [[ud objectForKey:@"cardMatrixColumns"] intValue];

Actually, this is such a common transformation when communicating with NSUserDefaults that convenience methods are provided. So I could have written the same thing this way:

NSUserDefaults* ud = [NSUserDefaults standardUserDefaults];

int therows = [ud integerForKey:@"cardMatrixRows"];

int thecols = [ud integerForKey:@"cardMatrixColumns"];

An NSNumber subclass, NSDecimalNumber, on the other hand, can be used in calculations, thanks to a bunch of arithmetic methods (or their C equivalent functions, which are faster). This is useful particularly for rounding, because there’s a handy way to specify the desired rounding behavior.

NSValue

NSValue is NSNumber’s superclass. Use it for wrapping nonnumeric C values such as structs where an object is expected — for storage in an NSArray, for example, or for use as a value with key–value coding.

Convenience methods provided through the NSValueUIGeometryExtensions category on NSValue (see the NSValue UIKit Additions Reference) allow easy wrapping and unwrapping of CGPoint, CGSize, CGRect, CGAffineTransform, UIEdgeInsets, and UIOffset; additional categories allow easy wrapping and unwrapping of NSRange, CATransform3D, CMTime, CMTimeMapping, CMTimeRange, MKCoordinate, and MKCoordinateSpan. You are unlikely to need to store any other kind of C value in an NSValue, but you can if you need to.

NSData

NSData is a general sequence of bytes; basically, it’s just a buffer, a chunk of memory. It is immutable; the mutable version is its subclass NSMutableData.

In practice, NSData tends to arise in two main ways:

§ When downloading data from the Internet. For example, NSURLConnection and NSURLSession supply whatever they retrieve from the Internet as NSData. Transforming it from there into (let’s say) a string, specifying the correct encoding, would then be up to you.

§ When storing an object as a file or in user preferences. For example, you can’t store a UIColor value directly into user preferences. So if the user has made a color choice and you need to save it, you transform the UIColor into an NSData (using NSKeyedArchiver) and save that:

§ [[NSUserDefaults standardUserDefaults] registerDefaults:

§ [NSDictionary dictionaryWithObjectsAndKeys:

§ [NSKeyedArchiver archivedDataWithRootObject:[UIColor blueColor]],

§ @"myColor",

nil]];

Equality and Comparison

The foregoing types will quickly come to seem to you like basic data types, but of course they are actually object types — which means that they are pointers (Chapter 3). Therefore you cannot compare them using the C operators for testing equality as you would with actual numbers. That’s because, in the case of object types, the C operators compare the pointers, not the object content of the instances. For example:

NSString* s1 = [NSString stringWithFormat:@"%@, %@", @"Hello", @"world"];

NSString* s2 = [NSString stringWithFormat:@"%@, %@", @"Hello", @"world"];

if (s1 == s2) // false

// ...

The two strings are equivalent (@"Hello, world") but are not the same object. (The example is deliberately elaborate because Cocoa’s efficient management of string literals sees to it that two strings initialized directly as @"Hello, world" are the same object, which wouldn’t illustrate the point I’m making.)

It is up to individual classes to implement a test for equality. The general test, isEqual:, is inherited from NSObject and overridden, but some classes also define more specific and efficient tests. Thus, the correct way to perform the above test is like this:

if ([s1 isEqualToString: s2])

Similarly, it is up to individual classes to supply ordered comparison methods. The standard method is called compare:, and returns one of three constants: NSOrderedAscending (the receiver is less than the argument), NSOrderedSame (the receiver is equal to the argument), orNSOrderedDescending (the receiver is greater than the argument); see Example 3-2.

NSIndexSet

NSIndexSet expresses a collection of unique whole numbers; its purpose is to express element numbers of an ordered collection, such as an NSArray. Thus, for instance, to retrieve multiple objects simultaneously from an array, you specify the desired indexes as an NSIndexSet. It is also used with other things that are array-like; for example, you pass an NSIndexSet to a UITableView to indicate what sections to insert or delete.

To take a specific example, let’s say you want to speak of elements 1, 2, 3, 4, 8, 9, and 10 of an NSArray. NSIndexSet expresses this notion in some compact implementation that can be readily queried. The actual implementation is opaque, but you can imagine that in this case the set might consist of two NSRange structs, {1,4} and {8,3}, and NSIndexSet’s methods actually invite you to think of an NSIndexSet as composed of ranges.

An NSIndexSet is immutable; its mutable subclass is NSMutableIndexSet. You can form a simple NSIndexSet consisting of just one contiguous range directly, by passing an NSRange to indexSetWithIndexesInRange:; but to form a more complex index set you’ll need to use NSMutableIndexSet so that you can append additional ranges.

To walk through (enumerate) the index values or ranges specified by an NSIndexSet, call enumerateIndexesUsingBlock: or enumerateRangesUsingBlock: or their variants.

NSArray and NSMutableArray

An NSArray is an ordered collection of objects. Its length is its count, and a particular object can be obtained by index number using objectAtIndex:. The index of the first object is zero, so the index of the last object is count minus one.

Starting with LLVM compiler version 4.0 (Xcode 4.5), it is no longer necessary to call objectAtIndex:; instead, you can use a notation reminiscent of C and other languages that have arrays, namely, append square brackets containing the index number to the array reference (subscripting).

So, for example, if pep consists of @"Manny", @"Moe", and @"Jack", then pep[2] yields @"Jack"; it is equivalent to [pep objectAtIndex:2]. Okay, I lied; actually, pep[2] is equivalent to [pep objectAtIndexedSubscript:2]. That’s because the subscripting notation causesany reference to which the subscript is appended to be sent objectAtIndexedSubscript:. This in turn means that any class can implement objectAtIndexedSubscript: and become eligible for subscripting notation — including your classes. Note that this method must be publicly declared for the compiler to permit the subscripting notation.

You can form an NSArray in various ways, but typically you’ll start by supplying a list of the objects it is to contain. As I mentioned in Chapter 3, a literal syntax (starting with LLVM compiler version 4.0, Xcode 4.4 or later) lets you wrap this list in @[...] as a way of generating the NSArray:

NSArray* pep = @[@"Manny", @"Moe", @"Jack"];

An NSArray is immutable. This doesn’t mean you can’t mutate any of the objects it contains; it means that once the NSArray is formed you can’t remove an object from it, insert an object into it, or replace an object at a given index. To do those things, you can derive a new array consisting of the original array plus or minus some objects, or use NSArray’s subclass, NSMutableArray.

NSMutableArray’s addObject: and replaceObjectAtIndex:withObject: are supplemented by the same subscripting notation that applies to NSArray. In this case, though, the subscripted reference is an lvalue — you’re assigning to it:

pep[3] = @"Zelda";

That causes the NSMutableArray to be sent setObject:atIndexedSubscript:. NSMutableArray implements this in such a way that, if pep has three elements, pep[3] = @"Zelda" is equivalent to addObject: (you’re appending to the end of the array), but if pep has more than three elements, it’s equivalent to replaceObjectAtIndex:withObject:. (If pep has fewer than three elements, an exception is thrown.)

You can walk through (enumerate) every object in an array with the for...in construct described in Chapter 1. (You’ll get an exception if you try to mutate an NSMutableArray while enumerating it.)

You can seek an object within an array with indexOfObject: or indexOfObjectIdenticalTo:; the former’s idea of equality is to call isEqual:, whereas the latter uses pointer equality.

Those familiar with other languages may miss such utility array functions as map, which builds a new array of the results of calling a method on each object in the array. (makeObjectsPerformSelector: requires a selector that returns no value, and enumerateObjectsUsingBlock:requires a block function that returns no value.) The usual workaround is to make an empty mutable array and then enumerate the original array, calling a method and appending each result to the mutable array (Example 10-1). It is also sometimes possible to use key–value coding as a mapsubstitute (see Chapter 12).

Example 10-1. Building an array by enumerating another array

NSMutableArray* marr = [NSMutableArray new];

for (id obj in myArray) {

id result = [obj doSomething];

[marr addObject: result];

}

There are many ways to search or filter an array using a block:

NSArray* pep = @[@"Manny", @"Moe", @"Jack"];

NSArray* ems =

[pep objectsAtIndexes: [pep indexesOfObjectsPassingTest:

^BOOL(id obj, NSUInteger idx, BOOL *stop) {

return ([(NSString*)obj rangeOfString:@"m"

options:NSCaseInsensitiveSearch].location == 0);

}]];

You can derive a sorted version of an array, supplying the sorting rules in various ways, or if it’s a mutable array, you can sort it directly; see Example 3-1 and Example 3-2.

NOTE

Forming a new array from some or all of the elements of an existing array is not an expensive operation. The objects constituting the elements of the first array are not copied; the new array consists merely of a new set of pointers to the already existing objects. The same is true for the other collection types I’m about to discuss.

NSSet and Friends

An NSSet is an unordered collection of distinct objects. “Distinct” means that no two objects in a set can return YES when they are compared using isEqual:. Learning whether an object is present in a set is much more efficient than seeking it in an array, and you can ask whether one set is a subset of, or intersects, another set. You can walk through (enumerate) a set with the for...in construct, though the order is of course undefined. You can filter a set, as you can an array. Indeed, much of what you can do with a set is parallel to what you can do with an array, except that of course you can’t do anything with a set that involves the notion of ordering.

To escape even that restriction, you can use an ordered set. An ordered set (NSOrderedSet) is very like an array, and the methods for working with it are very similar to the methods for working with an array — NSOrderedSet even implements objectAtIndexedSubscript:, so you can fetch an element by subscripting. But an ordered set’s elements must be distinct. An ordered set provides many of the advantages of sets: for example, as with an NSSet, learning whether an object is present in an ordered set is much more efficient than for an array, and you can readily take the union, intersection, or difference with another set. Since the distinctness restriction will often prove no restriction at all (because the elements were going to be distinct anyway), it is worthwhile to use NSOrderedSet instead of NSArray wherever possible.

NOTE

Handing an array over to an ordered set uniques the array, meaning that order is maintained but only the first occurrence of an equal object is moved to the set.

An NSSet is immutable. You can derive one NSSet from another by adding or removing elements, or you can use its subclass, NSMutableSet. Similarly, NSOrderedSet has its mutable counterpart, NSMutableOrderedSet (which implements setObject:atIndexedSubscript:). There is no penalty for adding to, or inserting into, a mutable set an object that the set already contains; nothing is added (and so the distinctness rule is enforced), but there’s no error.

NSCountedSet, a subclass of NSMutableSet, is a mutable unordered collection of objects that are not necessarily distinct (this concept is usually referred to as a bag). It is implemented as a set plus a count of how many times each element has been added.

NSDictionary and NSMutableDictionary

An NSDictionary is an unordered collection of key–value pairs (what some languages would refer to as a hash). The key is usually an NSString, though it doesn’t have to be. The value can be any object. An NSDictionary is immutable; its mutable subclass is NSMutableDictionary.

The keys of a dictionary are distinct (using isEqual: for comparison). If you add a key–value pair to an NSMutableDictionary, then if that key is not already present, the pair is simply added, but if the key is already present, then the corresponding value is replaced.

The fundamental use of an NSDictionary is to request an entry’s value by key (using objectForKey:); if no such key exists, the result is nil, so this is also the way to find out whether a key is present. A dictionary is thus an easy, flexible data storage device, an object-based analogue to a struct. Cocoa often uses a dictionary to provide you with an extra packet of named values, as in the userInfo of an NSNotification, the options: parameter of application:didFinishLaunchingWithOptions:, and so on.

The same Objective-C modernizations that brought us array literals and subscripting have brought us dictionary literals and subscripting. In addition to forming a dictionary from an array of objects and an array of keys (dictionaryWithObjects:forKeys:) or as a nil-terminated list of alternating objects and keys (dictionaryWithObjectsAndKeys:), a dictionary may be formed literally as a comma-separated list of key–value pairs, each key followed by a colon and the value, and wrapped in @{...}. Thus, recall our earlier NSUserDefaults example:

[[NSUserDefaults standardUserDefaults] registerDefaults:

[NSDictionary dictionaryWithObjectsAndKeys:

@4,

@"cardMatrixRows",

@3,

@"cardMatrixColumns",

nil]];

That can be rewritten like this:

[[NSUserDefaults standardUserDefaults] registerDefaults:

@{@"cardMatrixRows":@4, @"cardMatrixColumns":@3}];

To fetch a value from a dictionary by its key, instead of calling objectForKey:, you can subscript the key in square brackets to the dictionary reference: dict[key]. Similarly, to add a key–value pair to an NSMutableDictionary, instead of calling setObject:forKey:, you can assign to the subscripted dictionary reference. Parallel to NSArray, this is accomplished behind the scenes by calling objectForKeyedSubscript: and setObject:forKeyedSubscript:, and your own classes can declare these methods and be eligible for keyed subscripting notation.

Data structures such as an array of dictionaries, a dictionary of dictionaries, and so forth, are extremely common, and will often lie at the heart of an app’s functionality. Here’s an example from one of my own apps. The app bundle contains a text file laid out like this:

chapterNumber [tab] pictureName [return]

chapterNumber [tab] pictureName [return]

As the app launches, I load this text file and parse it into a dictionary, each entry of which has the following structure:

key: (chapterNumber, as an NSNumber)

value: [Mutable Array]

(pictureName)

(pictureName)

...

Thus we end up with all pictures for a chapter collected under the number of that chapter. This data structure is designed with its purpose in mind: it makes it trivially easy and extremely fast for me later to access all the pictures for a given chapter.

Here’s the actual code by which I parse the text file into that data structure. For each line of the text file, if the dictionary entry for that chapter number doesn’t exist, we create it, with an empty mutable array as its value. Whether that dictionary entry existed or not, it does now, and its value is a mutable array, so we append the picture name to that mutable array. Observe how this single typical example brings together many of the Foundation classes discussed in this section:

NSString* f = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"txt"];

NSError* err = nil;

NSString* s = [NSString stringWithContentsOfFile:f

encoding:NSUTF8StringEncoding

error:&err];

// error-checking omitted

NSMutableDictionary* d = [NSMutableDictionary new];

for (NSString* line in [s componentsSeparatedByString:@"\n"]) {

NSArray* items = [line componentsSeparatedByString:@"\t"];

NSInteger chnum = [items[0] integerValue];

NSNumber* key = @(chnum);

NSMutableArray* marr = d[key];

if (!marr) { // no such key, create key–value pair

marr = [NSMutableArray new];

d[key] = marr;

}

// marr is now a mutable array, empty or otherwise

NSString* picname = items[1];

[marr addObject: picname];

}

You can get from an NSDictionary a list of keys, a sorted list of keys, or a list of values. You can walk through (enumerate) a dictionary by its keys with the for...in construct, though the order is of course undefined. A dictionary also supplies an objectEnumerator, which you can use with the for...in construct to walk through just the values. You can also walk through the key–value pairs together using a block, and you can even filter an NSDictionary by a test against its values.

MIGRATING TO MODERN OBJECTIVE-C

If you have old code that you’d like to convert to use the modern NSNumber, NSArray, and NSDictionary literal Objective-C syntax, and to use array and dictionary subscripting, you can do so with no work whatever. Simply choose Edit → Refactor → Convert to Modern Objective-C Syntax.

NSNull

NSNull does nothing but supply a pointer to a singleton object, [NSNull null]. Use this singleton object to stand for nil in situations where an actual object is required and nil is not permitted. For example, you can’t use nil as the value of an element of a collection (such as NSArray, NSSet, or NSDictionary), so you’d use [NSNull null] instead.

Despite what I said earlier about equality, you can test an object against [NSNull null] using the C equality operator, because this is a singleton instance and therefore pointer comparison works.

Immutable and Mutable

Beginners sometimes have difficulty with the Foundation’s immutable/mutable class pairs, so here are some hints.

The documentation may not make it completely obvious that the mutable classes obey and, if appropriate, override the methods of the immutable classes. Thus, for example, [NSArray array] generates an immutable array, but [NSMutableArray array] generates a mutable array. (You will look in vain for the expected [NSMutableArray mutableArray].) The same is true of all the initializers and convenience class methods for instantiation: they may have “array” in their name, but when sent to NSMutableArray, they yield a mutable array.

That fact also answers the question of how to make an immutable array mutable, and vice versa. If arrayWithArray:, sent to the NSArray class, yields a new immutable array containing the same objects in the same order as the original array, then the same method, arrayWithArray:, sent to the NSMutableArray class, yields a mutable array containing the same objects in the same order as the original. Thus this single method can transform an array between immutable and mutable in either direction. You can also use copy (produces an immutable copy) andmutableCopy (produces a mutable copy).

All of the above applies equally, of course, to the other immutable/mutable class pairs. You will often want to work internally and temporarily with a mutable instance but then store (and possibly vend to other classes) an immutable instance, thus protecting the value from being changed accidentally or behind your own back. What matters is not a variable’s declared class but what class the instance really is (polymorphism; see Chapter 5), so it’s good that you can easily switch between an immutable and a mutable version of the same data.

To test whether an instance is mutable or immutable, do not ask for its class. These immutable/mutable class pairs are all implemented as class clusters, which means that Cocoa uses a secret class, different from the documented class you work with. This secret class is subject to change without notice, because it’s none of your business and you should never have looked at it in the first place. Thus, code of this form is subject to breakage:

if ([NSStringFromClass([n class]) isEqualToString: @"NSCFArray"]) // wrong!

Instead, to learn whether a collection is mutable, ask it whether it responds to a mutability method:

if ([n respondsToSelector:@selector(addObject:)]) // right

(Unfortunately, that technique works only for collections; it doesn’t work, say, for distinguishing an NSString from an NSMutableString.)

NOTE

Here’s a reminder: just because a collection class is immutable doesn’t mean that the objects it collects are immutable. They are still objects and do not lose any of their normal behavior merely because they are pointed to by way of an immutable collection.

Property Lists

A property list is a string (XML) representation of data. The Foundation classes NSString, NSData, NSArray, and NSDictionary are the only classes that can be converted into a property list. Moreover, an NSArray or NSDictionary can be converted into a property list only if the only classes it collects are these classes, along with NSDate and NSNumber. (This is why, as I mentioned earlier, you must convert a UIColor into an NSData in order to store it in user defaults; the user defaults is a property list.)

The primary use of a property list is to store data as a file. NSArray and NSDictionary provide convenience methods writeToFile:atomically: and writeToURL:atomically: that generate property list files given a pathname or file URL, respectively; they also provide inverse convenience methods that initialize an NSArray object or an NSDictionary object based on the property list contents of a given file. For this very reason, you are likely to start with one of these classes when you want to create a property list. (NSString and NSData, with their methodswriteToFile:... and writeToURL:..., just write the data out as a file directly, not as a property list.)

When you generate an NSArray or NSDictionary object from a property list file in this way, the collections, string objects, and data objects in the collection are all immutable. If you want them to be mutable, or if you want to convert an instance of one of the other property list classes to a property list, you’ll use the NSPropertyListSerialization class (see the Property List Programming Guide).

The Secret Life of NSObject

Because every class inherits from NSObject, it’s worth taking some time to investigate and understand NSObject. NSObject is constructed in a rather elaborate way:

§ It defines some native class methods and instance methods having mostly to do with the basics of instantiation and of method sending and resolution. (See the NSObject Class Reference.)

§ It adopts the NSObject protocol. This protocol declares instance methods having mostly to do with memory management, the relationship between an instance and its class, and introspection. Because all the NSObject protocol methods are required, the NSObject class implements them all. (See the NSObject Protocol Reference.) This architecture is what permits NSProxy to be a root class; it, too, adopts the NSObject protocol.

§ It implements convenience methods related to the NSCopying, NSMutableCopying, and NSCoding protocols, without formally adopting those protocols. NSObject intentionally doesn’t adopt these protocols because this would cause all other classes to adopt them, which would be wrong. But thanks to this architecture, if a class does adopt one of these protocols, you can call the corresponding convenience method. For example, NSObject implements the copy instance method, so you can call copy on any instance, but you’ll crash unless the instance’s class adopts the NSCopying protocol and implements copyWithZone:.

§ A large number of methods are injected into NSObject by more than two dozen categories on NSObject, scattered among various header files. For example, awakeFromNib (see Chapter 7) comes from the UINibLoadingAdditions category on NSObject, declared in UINibLoading.h. AndperformSelector:withObject:afterDelay:, discussed in Chapter 11, comes from the NSDelayedPerforming category on NSObject, declared in NSRunLoop.h.

§ A class object, as explained in Chapter 4, is an object. Therefore all classes, which are objects of type Class, inherit from NSObject. Therefore, any method defined as an instance method by NSObject can be called on a class object as a class method! For example,respondsToSelector: is defined as an instance method by NSObject, but it can (therefore) be treated also as a class method and sent to a class object.

The problem for the programmer is that Apple’s documentation is rather rigid about classification. When you’re trying to work out what you can say to an object, you don’t care where that object’s methods come from; you just care what you can say. But the documentation differentiates methods by where they come from. Even though NSObject is the root class, the most important class, from which all other classes inherit, no single page of the documentation provides a conspectus of all its methods. Instead, you have to look at both the NSObject Class Reference and theNSObject Protocol Reference simultaneously, plus the pages documenting the NSCopying, NSMutableCopying, and NSCoding protocols (in order to understand how they interact with methods defined by NSObject), plus you have to supply mentally a class method version of every NSObject instance method!

Then there are the methods injected into NSObject by categories. Some that are general in nature are documented on the NSObject class documentation page itself; for example, cancelPreviousPerformRequestsWithTarget: comes from a category declared in NSRunLoop.h, but it is documented under NSObject, quite rightly, since this is a class method, and therefore effectively a global method, that you might want to send at any time. Others are delegate methods used in restricted situations (so that these are really informal protocols), and do not need centralized documentation; for example, animationDidStart: is documented under the CAAnimation class, quite rightly, because you need to know about it only and exactly when you’re working with CAAnimation. However, every object responds to awakeFromNib, and it’s likely to be crucial to every app you write; yet you must learn about it outside of the NSObject documentation, sitting all by itself in the NSObject UIKit Additions Reference page, where you’re extremely unlikely to discover it! The same goes, it might be argued, for all the key–value coding methods (Chapter 12) and key–value observing methods (Chapter 13).

Once you’ve collected, by hook or by crook, all the NSObject methods, you can see that they fall into a certain natural classification, much as outlined in Apple’s documentation (see also “The Root Class” in the “Cocoa Objects” section of the Cocoa Fundamentals Guide):

Creation, destruction, and memory management

Methods for creating an instance, such as alloc and copy, along with methods that you might override in order to learn when something is happening in the lifetime of an object, such as initialize (see Chapter 11) and dealloc (see Chapter 12), plus methods that manage memory (see Chapter 12).

Class relationships

Methods for learning an object’s class and inheritance, such as class, superclass, isKindOfClass:, and isMemberOfClass:.

To check the class of an instance (or class), use methods such as isKindOfClass: and isMemberOfClass:. Direct comparison of two class objects, as in [someObject class] == [otherObject class], is rarely advisable, especially because a Cocoa instance’s class might be a private, undocumented subclass of the class you expect. I mentioned this already in connection with class clusters, and it can happen in other cases.

Object introspection and comparison

Methods for asking what would happen if an object were sent a certain message, such as respondsToSelector:; for representing an object as a string (description, used in debugging; see Chapter 9); and for comparing objects (isEqual:).

Message response

Methods for meddling with what does happen when an object is sent a certain message, such as doesNotRecognizeSelector:. If you’re curious, see the Objective-C Runtime Programming Guide.

Message sending

Methods for sending a message dynamically. For example, performSelector: takes a selector as parameter, and sending it to an object tells that object to perform that selector. This might seem identical to just sending that message to that object, but what if you don’t know what message to send until runtime? Moreover, variants on performSelector: allow you to send a message on a specified thread, or send a message after a certain amount of time has passed (performSelector:withObject:afterDelay: and similar).