http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocSelectors.html#//apple_ref/doc/uid/TP30001163-CH23-SW1
Selectors
In Objective-C, selector has two meanings. It can be used to refer simply to the name of a method when it’s used in a source-code message to an object. It also, though, refers to the unique identifier that replaces the name when the source code is compiled. Compiled selectors are of type SEL
. All methods with the same name have the same selector. You can use a selector to invoke a method on an object—this provides the basis for the implementation of the target-action design pattern in Cocoa.
Methods and Selectors
For efficiency, full ASCII names are not used as method selectors in compiled code. Instead, the compiler writes each method name into a table, then pairs the name with a unique identifier that represents the method at runtime. The runtime system makes sure each identifier is unique: No two selectors are the same, and all methods with the same name have the same selector.
SEL and @selector
Compiled selectors are assigned to a special type, SEL
, to distinguish them from other data. Valid selectors are never 0
. You must let the system assign SEL
identifiers to methods; it’s futile to assign them arbitrarily.
The @selector()
directive lets you refer to the compiled selector, rather than to the full method name. Here, the selector for setWidth:height:
is assigned to the setWidthHeight
variable:
SEL setWidthHeight; |
setWidthHeight = @selector(setWidth:height:); |
It’s most efficient to assign values to SEL
variables at compile time with the @selector()
directive. However, in some cases, you may need to convert a character string to a selector at runtime. You can do this with the NSSelectorFromString
function:
setWidthHeight = NSSelectorFromString(aBuffer); |
Conversion in the opposite direction is also possible. The NSStringFromSelector
function returns a method name for a selector:
NSString *method; |
method = NSStringFromSelector(setWidthHeight); |
Methods and Selectors
Compiled selectors identify method names, not method implementations. The display
method for one class, for example, has the same selector as display
methods defined in other classes. This is essential for polymorphism and dynamic binding; it lets you send the same message to receivers belonging to different classes. If there were one selector per method implementation, a message would be no different from a function call.
A class method and an instance method with the same name are assigned the same selector. However, because of their separate domains, there’s no confusion between the two. A class could define adisplay
class method in addition to a display
instance method.
Method Return and Parameter Types
The messaging routine has access to method implementations only through selectors, so it treats all methods with the same selector alike. It discovers the return type of a method, and the data types of its parameters, from the selector. Therefore, except for messages sent to statically typed receivers, dynamic binding requires all implementations of identically named methods to have the same return type and the same parameter types. (Statically typed receivers are an exception to this rule because the compiler can learn about the method implementation from the class type.)
Although identically named class methods and instance methods are represented by the same selector, they can have different parameter types and return types.
Varying the Message at Runtime
The performSelector:
, performSelector:withObject:
, and performSelector:withObject:withObject:
methods, defined in the NSObject
protocol, take SEL
identifiers as their initial parameters. All three methods map directly into the messaging function. For example,
[friend performSelector:@selector(gossipAbout:) |
withObject:aNeighbor]; |
is equivalent to:
[friend gossipAbout:aNeighbor]; |
These methods make it possible to vary a message at runtime, just as it’s possible to vary the object that receives the message. Variable names can be used in both halves of a message expression:
id helper = getTheReceiver(); |
SEL request = getTheSelector(); |
[helper performSelector:request]; |
In this example, the receiver (helper
) is chosen at runtime (by the fictitious getTheReceiver
function), and the method the receiver is asked to perform (request
) is also determined at runtime (by the equally fictitious getTheSelector
function).
Note: performSelector:
and its companion methods return an object of type id
. If the method that’s performed returns a different type, it should be cast to the proper type. (However, casting doesn’t work for all types; the method should return a pointer or a type compatible with a pointer.)
The Target-Action Design Pattern
In its treatment of user-interface controls, AppKit makes good use of the ability to vary both the receiver and the message at runtime.
NSControl
objects are graphical devices that can be used to give instructions to an application. Most resemble real-world control devices such as buttons, switches, knobs, text fields, dials, menu items, and the like. In software, these devices stand between the application and the user. They interpret events coming from hardware devices such as the keyboard and mouse and translate them into application-specific instructions. For example, a button labeled “Find” would translate a mouse click into an instruction for the application to start searching for something.
AppKit defines a template for creating control devices and defines a few off-the-shelf devices of its own. For example, the NSButtonCell
class defines an object that you can assign to an NSMatrix
instance and initialize with a size, a label, a picture, a font, and a keyboard shortcut. When the user clicks the button (or uses the keyboard shortcut), the NSButtonCell
object sends a message instructing the application to do something. To do this, an NSButtonCell
object must be initialized not just with an image, a size, and a label, but with directions on what message to send and who to send it to. Accordingly, an NSButtonCell
instance can be initialized for an action message (the method selector it should use in the message it sends) and a target (the object that should receive the message).
[myButtonCell setAction:@selector(reapTheWind:)]; |
[myButtonCell setTarget:anObject]; |
When the user clicks the corresponding button, the button cell sends the message using the NSObject
protocol method performSelector:withObject:
. All action messages take a single parameter, theid
of the control device sending the message.
If Objective-C didn’t allow the message to be varied, all NSButtonCell
objects would have to send the same message; the name of the method would be frozen in the NSButtonCell
source code. Instead of simply implementing a mechanism for translating user actions into action messages, button cells and other controls would have to constrain the content of the message. Constrained messaging would make it difficult for any object to respond to more than one button cell. There would either have to be one target for each button, or the target object would have to discover which button the message came from and act accordingly. Each time you rearranged the user interface, you would also have to reimplement the method that responds to the action message. An absence of dynamic messaging would create an unnecessary complication that Objective-C happily avoids.
Avoiding Messaging Errors
If an object receives a message to perform a method that isn’t in its repertoire, an error results. It’s the same sort of error as calling a nonexistent function. But because messaging occurs at runtime, the error often isn’t evident until the program executes.
It’s relatively easy to avoid this error when the message selector is constant and the class of the receiving object is known. As you write your programs, you can make sure that the receiver is able to respond. If the receiver is statically typed, the compiler performs this test for you.
However, if the message selector or the class of the receiver varies, it may be necessary to postpone this test until runtime. The respondsToSelector:
method, defined in the NSObject
class, tests whether a receiver can respond to a message. It takes the method selector as a parameter and returns whether the receiver has access to a method matching the selector:
if ( [anObject respondsToSelector:@selector(setOrigin::)] ) |
[anObject setOrigin:0.0 :0.0]; |
else |
fprintf(stderr, "%s can’t be placed\n", |
[NSStringFromClass([anObject class]) UTF8String]); |
The respondsToSelector:
runtime test is especially important when you send messages to objects that you don’t have control over at compile time. For example, if you write code that sends a message to an object represented by a variable that others can set, you should make sure the receiver implements a method that can respond to the message.
Note: An object can also arrange to have messages it receives forwarded to other objects if it doesn’t respond to them directly itself. In that case, from the caller’s perspective, the object appears to handle the message directly, even though it handles the message indirectly by forwarding it to another object. See “Message Forwarding” in Objective-C Runtime Programming Guide for more information.