Objective-C Programming_ The Big Nerd Ranch Guide - Aaron Hillegass [53]
In a real-world application, there needs to be an object that waits for events like mouse movements, touch events, timers, and network activity. On Mac OS X and iOS, this object is an instance of NSRunLoop. The run loop sits and waits, and when something happens, it sends a message to another object.
We say that when something happens, the run loop causes a callback to occur. For Objective-C programmers, there are three forms that a callback can take. (Because these are very general ideas, I am going to use x for “a specific something” that happens. I’ll fill in the details in the sections that follow.)
Target-action: Before the wait begins, you say “When x happens, send this particular message to that particular object.” The object receiving the message is the target. The selector for the message is the action.
Helper objects: Before the wait begins, you say “Here is a helper object that conforms to your protocol. Send it messages when things happen.” (More on protocols in Chapter 25.) Helper objects are often known as delegates or data sources.
Notifications: There is an object called the notification center. Before the wait begins, you say to the notification center “This object is waiting for these sorts of notifications. When one of those notifications arrives, send the object this message.” When x happens, an object posts a notification to the notification center, and the center forwards it on to your object.
In this chapter, you will implement all three types of callbacks and learn which to employ in which circumstances.
Target-action
Timers use a target-action mechanism. You create a timer with a delay, a target, and an action. After that delay, the timer sends the action message to its target.
You are going to create a program with a run loop and timer. Every two seconds, the timer will send the action message to its target. You will create a class, and an instance of that class will be the target.
Figure 24.1 Logger is the target of the NSTimer
In Xcode, create a new project: a Foundation Command Line Tool named Callbacks. As a goof, first you are just going to get a run loop and start it running. Edit main.m:
#import int main (int argc, const char * argv[]) { @autoreleasepool { [[NSRunLoop currentRunLoop] run]; } return 0; } Build and run the program. Notice that the method run never returns. The run loop is in an infinite loop waiting for something to happen. You’ll need to terminate the program. (Choose Product → Stop.) Now you are going to create a custom class to act as the target of the timer. Create a new file: an Objective-C class called Logger that is a subclass of NSObject. (Remember, to get to the class template, choose File → New → New File....) In Logger.h, declare the action method: #import @interface Logger : NSObject - (void)sayOuch:(NSTimer *)t; @end Notice that the action method takes one argument – the object that is sending the action message. In this case, it is the timer object. Implement a simple sayOuch: method in Logger.m: #import "Logger.h" @implementation Logger - (void)sayOuch:(NSTimer *)t { NSLog(@"Ouch!"); } @end At this point, we need to take a short detour and discuss selectors. Remember that when you send a message to an object, the object’s class is asked if it has a method with that name. The search goes up the inheritance hierarchy until a class responds with “Yeah, I’ve got a method with that name.” Figure 24.2 The search for a method with the right name As you can imagine, this search has to happen very, very quickly. If we used the actual name of the method (which could be very long), method lookup would be really slow. To speed things up, the compiler assigns a unique number to each method name it encounters. At runtime, we use that number instead of the method name. Figure 24.3 How it really works That unique number that represents a particular method name is known as a selector. To create a timer that sends