Hour 12. Routing Messages with Selectors


What You’ll Learn in This Hour

Image Understanding the target-action pattern

Image Using SEL and @selector

Image Invoking selectors with NSInvocation

Image Managing messages with runtime tools


Getting Inside Objective-C Messages

By now, you should be familiar with Objective-C being described as being a dynamic messaging environment, and you have seen examples of messages in Hour 3, “Using Object-Oriented Features in Objective-C,” and in Hour 6, “Exploring Messaging and a Testbed App.” This hour looks inside the messages to see how they work. Because this is the heart of Objective-C and also because this is a significant way in which it differs from many other object-oriented programming languages, it is worthwhile to spend some time on the concepts.

In the simple examples shown in Hours 3 and 6, you saw messages that were used somewhat in the way in which functions are used in other languages, although, as noted, messaging is very different from calling functions. The similarity in the examples is that they are sent at a specific place in the code. In Objective-C, you send a message at a certain point in the program’s execution, and in other languages at that point you call a function.

However, in Objective-C, you can turn a message into an object that can then be executed directly, but you can also send that object to an object such as an instance of an NSTimer that takes care of invoking it at appropriate times. By being able to in effect convert the message to a reusable object, you have a tremendous degree of flexibility. Furthermore, the object contains properties that you can set and reset as the need arises. You see how to do this in the course of this hour.

GO TO Image A related use of messages is found in Hour 22, “Grand Central Dispatch: Using Queues and Threading,” page 283.


Objective-C’s Predecessor: Smalltalk

Smalltalk grew out of a research project led by Alan Kay at Xerox PARC in the 1970s. It was first widely released in 1980, hence the name of the most common variant, Smalltalk-80. It is built on objects and messaging.

Objective-C represents the combination of the Smalltalk style of object-oriented programming with the strategy of making the fewest modifications to C in order to implement an object-oriented language. In fact, Brad J. Cox, who worked with Tom Love to develop Objective-C, wrote in his book, Object-Oriented Programming: An Evolutionary Approach, that “Objective-C is a hybrid programming language...formed by grafting the Smalltalk-80 style of object-oriented programming onto a C language rootstock.”

That combination was historically important because for many people, Smalltalk had a reputation as being a great concept and teaching tool but not a tool that many people wanted to use for development.

On the other hand, many people found C++ to be too much of an extension to C to the point that it became not so much an extension but a new language. There were (and in some circles still are) fierce arguments over the relative merits of Smalltalk, C++, and Objective-C.

This book deals with the use of Objective-C, so those discussions are beyond its scope. However, it is important to note that messaging is at the heart of Objective-C and that it was implemented in previous languages, most notably Smalltalk. There are a number of books, articles, and web resources about Smalltalk that can help you understand some of the features and design decisions in Objective-C, but remember that this is background and history. Smalltalk syntax is not Objective-C syntax, although there are many similarities.


In Hour 3, you saw simple examples of messages such as the following:

myVariable = [myObject init];

The high-level description of messaging in Hour 3 identifies the right side of the expression as the message; the result of the message is used in the expression to set the value of myVariable.

Receiver and Selector Objects in Messages

Now it is time to look inside the message and to name the two words shown in this example:

Image Receiver is the object to which the message is sent (myObject, in this case).

Image Selector is the command sent to the receiver (init, in this case).

At runtime, the selector is used to identify the specific method of the object that is to be performed. This distinction between the selector and the method of the receiver that it selects at runtime helps to provide much of the flexibility and dynamism of Objective-C. It is made possible by the Objective-C runtime, which is the topic of the following section.

Getting Inside the Objective-C Runtime

The flexibility described in the previous section is at the heart of many debates, but, as is so often the case, advances in processor speed and memory availability have made many of those arguments much less relevant today. The issue is that when a function call is coded in a statically typed language, the compiler can check to make certain that the object that is asked to perform the function actually has such a function available. With the dynamic messaging environment, the message (specifically the selector) is sent to a receiver, and it is quite possible that the receiver will not be able to perform the method.

In many cases (particularly if the receiver is declared to be of type id, meaning any object), there is no way to perform this check at compile time. At its simplest, the argument has been whether this error checking should be done once at compile time or every time a message is sent at runtime. With the advances on the hardware side and several decades of experience optimizing the dispatching of methods in response to selectors, the flexibility of the messaging model in Objective-C has won out at least in the OS X and iOS environments.


Note: The Runtime

The shared library that implements the runtime component of Objective-C programs is commonly referred to simply as the runtime.


Here, for example, is the function call that is generated by the compiler on OS X when it encounters a message call in your code:

id objc_msg (id theReceiver, SEL theSelector, ...)

You can see that it is dynamic: theReceiver is a generic id type, as is the result of the function. theSelector and the SEL type are described in the following section.

Although you rarely use the shared library directly (the most common reason is if you are programming cross-language projects), it is there when your app is running and, at runtime, becomes a part of your app. This does not mean that you can deal with all of your app’s bugs by blaming them on the runtime code. In fact, if it is involved with bugs at all, it is most likely an innocent bystander.

Rather, you need to remember that if you are coming from compiler-centric languages, you are gaining a tremendous degree of flexibility with Objective-C and its runtime, but you are also losing a safety net that compilers with strict type checking and compile time can provide.

There are two consequences of this that you should remember:

Image More than ever before, it is important to catch and correct errors at the earliest possible opportunity. Xcode helps you with its tools to catch syntax errors based on the underlying Objective-C syntax as well as catching some errors that would only be caught at compile time in other environments. Many people are used to writing code and then debugging it in a second step, but today it is common practice to merge those processes into one step. (This is true for other modern programming languages as well as Objective-C.)

Image You are not at the mercy of what happens at runtime. There are methods you can call to test if a message you are about to send will generate an error or not. They are described later in this hour.

Working with SEL and @selector ()

Just as there have been concerns about memory management in object-oriented programs, there have also been concerns about performance. As noted, one of the key areas in which performance can be compromised is in the selection and dispatching of methods. Developers of the Objective-C language and its compilers have focused on this area. Method names for a program are stored in a table where they can be looked up as needed. Each is associated with a unique identifier, and it is that unique identifier that is used for dispatching. This avoids handling long strings of characters to identify methods that are (properly) labeled descriptively.

You can gain access to these identifiers indirectly and can not only optimize your code, but also add some additional dynamism to it. To accomplish this, a special type, SEL, has been created. You use it to type identifiers just as you would type an identifier as an int or float.

But what value can you assign to a SEL? The answer is simple: You assign to a SEL identifier the selector you would use in a message. There are two variations on the way that you do this; they differ in the way they handle arguments to the selector. Both of these mechanisms are described in the sections that follow.

Furthermore, you can use both mechanisms in NSTimer methods where you set up a timer to run repeatedly.


Tip: Compiled Versus Computed Selectors

When you write a message such as the following

myVariable = [myObject init];

you specify the receiver and the selector. As is the case with the other code that you write, the compiler generates the code that runs. The selector is referred to as a compiled selector.

By contrast, you can use a selector that is evaluated at runtime from a string that can be generated on the fly. This is referred to as a computed selector.


Using performSelector

For zero, one, or two arguments, you create a SEL that you can subsequently pass into one of the performSelector methods. The performSelector methods are further limited in that their one or two arguments must be of type id. Even with these limitations, they handle many (perhaps most) of the selectors you use in common processing.

Creating a Selector with @selector ()

There are several ways of creating a compiled selector. The simplest way is to use the @selector compiler directive. If that is the route you take, you just type in the code you would have used in a message. Thus, for a message such as

myVariable = [myObject myAction];

you can generate a compiled selector with

SEL mySelector;
mySelector = @selector(myAction);

If you have arguments for the method that you are calling, you supply their keywords but not their values, as follows:

mySelector = @selector(myAction: forPurpose:);

If there are two arguments, you supply both of them, as follows:

mySelector = @selector(myAction: forPurpose: withCondition:);

Creating a Selector from a String

Instead of hard-coding the selector in your code, you might want to set it from a variable string at runtime. (These are computed selectors.) There is a utility function for that. The first example in the previous section can be rewritten as the following:

NSString * myString;
SEL mySelector;

// set myString from user input, options, a data store, etc.

mySelector = NSSelectorFromString (myString);

If you are storing a selector’s string for reuse at a later time, you can convert a selector to a string:

myString = NSStringFromSelector (myAction: forPurpose:);

Using a Selector

After you have a selector, you can perform it with any of these three methods (they are declared in the NSObject protocol so they are available to any Cocoa classes):

– (id)performSelector:(SEL)aSelector;

– (id)performSelector:(SEL)aSelector withObject:(id)anObject;

– (id)performSelector:(SEL)aSelector withObject:(id)anObject
  withObject:(id)anotherObject;

If you use the second or third version, the selector must take parameters of type id. If you want to use more parameters or if you want to use a selector that does not take id parameters, you use NSInvocation, as described in the following section.

performSelector is used for the selector in a message, as follows:

[receiver performSelector:mySelector];

If you are using parameters (the second or third version), you pass the id in the objects and the message is put together with the keywords and parameters.


Tip

Just remember the colon before each parameter in the @selector directive. Leaving it out is a very common mistake.


These methods are declared in the NSObject protocol. Related methods are declared directly in NSObject, the root class of Cocoa.


Note

The NSObject performSelector methods are listed here so that you can get a sense of the opportunities you have in the Cocoa framework, which itself is beyond the scope of this book.

Click here to view code image

– performSelector:withObject:afterDelay:
– performSelector:withObject:afterDelay:inModes:
– performSelectorOnMainThread:withObject:waitUntilDone:
– performSelectorOnMainThread:withObject:waitUntilDone:modes:
– performSelector:onThread:withObject:waitUntilDone:
– performSelector:onThread:withObject:waitUntilDone:modes:
– performSelectorInBackground:withObject:
+ cancelPreviousPerformRequestsWithTarget:
+ cancelPreviousPerformRequestsWithTarget:selector:object:


In addition, you can use the NSTimer subclass of NSObject to perform selectors.


Using a Selector with Interface Builder

Although this book focuses on the Objective-C language itself, a brief excursion into the application frameworks is relevant here. Selectors lie at the heart of the process by which interface elements, such as menu commands and buttons, perform their actions using the Interface Builder editor in Xcode; you can connect an interface element to an action that is declared as a method in an object.

The object that declares the method, and therefore is responsible for performing the action, can be a specific object such as a document. However, Xcode and the runtime both keep track of whatever object happens to be the first in line to process events such as mouse clicks. This object is the first responder and it constantly changes as the user interacts with the program.

The Interface Builder editor keeps track of all actions of all objects that potentially could become the first responder. You can control-drag from an interface element to the list of these potential first-responder actions. If you Control-Click or Right-Click (depending on your trackpad or mouse settings) on First Responder in the document outline, as shown in Figure 12.1, you see the first responders. You can connect one of them to an interface element such as a button or (on OS X) a menu command.


Image

FIGURE 12.1 Connect an interface element to an action.

Using NSInvocation

For three or more arguments, you use an NSInvocation object. In addition to being able to handle more arguments, you are not limited to using arguments of type id as long as they are classes (in other words, no ints or chars). With performSelector, you specify the receiver and action as arguments to performSelector. NSInvocation is a more sophisticated mechanism in which you convert a message to an object. NSInvocation objects have properties that specify the target, selector, arguments, and return value. This flexibility lets you use NSInvocation objects with much more flexibility than when you use performSelector.

There are situations in which a common use of NSInvocation instances is to use and reuse the same selector a number of times in different contexts. You can, for example, set up a selector and vary its target (that is, the receiver of the message). You can also change arguments. However, the one thing you cannot change is the method signature of the selector. That is set when you create the object, and it cannot be varied.


Method Signatures

In Objective-C, a method signature consists of the method name along with parameter names and colons (if needed). Thus, you can have insertObject:atIndex:, which is a method that takes two parameters.


Although NSInvocation objects are more flexible than performSelector, in practice you might not need them very often because the simpler interface of performSelector handles many of the common cases. If you do want to use an NSInvocation object, it is summarized in this section. There are three sets of methods that enable you to do the following:

Image Create an NSInvocation instance.

Image Set and get its properties.

Image Invoke the instance (that is, perform the selector).

Creating an NSInvocation

Although you can change every property of an NSInvocation instance, you cannot change the message signature of the instance you have created. You create it using the class method shown here:

+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)signature

Using NSInvocation Properties

After you have your NSInvocation instance, you set its properties. You can use a newly created instance, or you can reuse an existing one if it is created for the right message signature. This is most commonly done if you want to reinvoke the invocation with a minor modification of its target or arguments or at a different time. The following sections cover the setters you use.

GO TO Image Hour 16, “Managing Memory and Runtime Objects,” p. 221, for more information on memory aspects of NSInvocation.

Setting the Target

You use setTarget to set the target. Here is the declaration:

– (void)setTarget:(id)anObject

Following on from the NSInvocation created in the Try It Yourself activity, if you have an instance of MyClass called myInstance, you can write:

[myInvocation setTarget:myInstance];

The corresponding getter is:

– (id)target

Setting the Selector

You can set the selector as long as it is compatible with the message signature you used in creating the NSInvocation instance. You use setSelector; here is the declaration:

– (void)setSelector:(SEL)selector

You frequently see the same selector used here as was used in creating the message signature. That certainly ensures compatibility because they are identical, but as long as they are compatible, you are fine:

[myInvocation setSelector:@selector(myMethod:)];

The corresponding getter is:

– (SEL)selector

Setting the Arguments

The arguments for the NSInvocation instance are in a buffer. The zeroth element is the target, and the first is the selector. You use the accessors just described to access those, so you start working with element 2 of the array. Here is the declaration to set an argument:

– (void)setArgument:(void *)buffer atIndex:(NSInteger)index

When you are setting an argument to an object, remember to pass in a pointer to it, as shown in the following line of code:

[myInvocation setArgument:&myObjectForArgument atIndex:2];

The corresponding getter is:

– (void)getArgument:(void *)buffer atIndex:(NSInteger)index

Remember that you pass in a pointer so that to retrieve the same argument set in the previous code, you write:

[myInvocation getArgument:&anArray atIndex:2];

Getting the Result

When you send a message in your code, you receive the result back and can assign it to a variable, or you can use it in an expression. When you are invoking a message at some time in the future, you have to retrieve the result from the invocation. Here is the getter:

– (void)getReturnValue:(void *)buffer

You have two ways of using this getter: one for objects and another one for C variables other than pointers and objects.

In the case of an object, you declare it in the code where you want to access the result just as you normally would. You then get it from the NSInvocation and pass in a pointer to your variable as in this code:

[myInvocation getReturnValue:&myLocallyDeclaredVariable];

If the result is not an object, you allocate a buffer of the right size and pass that in. You can get the size of the returned value by asking the method signature. And, because you used the method signature in creating your NSInvocation instance, you can ask that instance for the signature. Here is an example:

NSUInteger length = [[myInvocation methodSignature] methodReturnLength];
buffer = (void *)malloc(length);
[myInvocation getReturnValue:buffer];

Invoking an NSInvocation

After you have your instance of NSInvocation and have set its target, selector, and arguments, you can invoke it with the following:

– (void)invoke

You simply write:

[myInvocation invoke];

You can short-circuit the process by using the following:

– (void)invokeWithTarget:(id)anObject

This means you can write:

[myInvocation invokeWithTarget: myInstance];

In this case, you need to set the selector and the arguments, but you do not need to set the target separately.

In addition to these methods, you can use the NSTimer subclass of NSObject to invoke NSInvocation instances.

Testing Whether an Instance Can Respond to a Selector

As noted previously, the messaging structure in Objective-C is implemented in large part in the runtime. This provides a great deal of flexibility, but it also means that you can generate errors at runtime that, in other languages, would have been caught at compile time. This is not merely a matter of the error appearing at a different time. It means in most cases that the error is seen by the end user rather than by the developer.

To avoid such circumstances, you can check to see if a class can respond to a selector. Here is the declaration:

– (BOOL)respondsToSelector:(SEL)aSelector

You can write:

BOOL myBool = [self.myCurrencyConverter respondsToSelector:@selector (calculate);

or

BOOL myBool = [self.myCurrencyConverter respondsToSelector: mySelector];

This is declared in the NSObject protocol, so it is available for all instances of all classes.

A companion class method lets you test a class:

+ (BOOL)instancesRespondToSelector:(SEL)aSelector

Summary

Messages are at the core of Objective-C and represent one of the biggest differences between it and some other object-oriented programming languages. In this hour, you have examined how messages are constructed as well as how you can construct them yourself in code. You have also seen how you can perform and invoke them not only when you need to, but also as prepackaged messages that can be sent as the need arises.

Q&A

Q. How do you choose between using performSelector and using NSInvocation?

A. performSelector works for messages with zero, one, or two arguments. Also, they must be of type id and must be objects.

Q. What is the runtime?

A. It is the data structures and functions that implement the runtime dynamic features of Objective-C. It is not optional, but it’s there for you and you rarely need to worry about it. It is written mostly in C and Assembler. It provides the underpinnings of Objective-C but is written in previous languages.

Workshop

Quiz

1. How do you make certain that a message you are sending is valid?

2. NSInvocation converts an Objective-C message to what?

Quiz Answers

1. Package it in a selector and test it against the receiver with respondsToSelector.

2. NSInvocation converts an Objective-C message to an object.

Activities

Explore the sample code on developer.apple.com to see how you can use selectors. Good choices are the following:

Image Use any of the Xcode storyboard templates on iOS and search for the code that implements bar button items. (Often it is in viewDidLoad.) You’ll see how selectors are attached to these buttons.

Image Look at the MenuMadness sample code to see how selectors are attached to menu commands on OS X.

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

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