This section explains the syntax of sending messages, including how you can nest message expressions. It also discusses the “visibility” of an object’s instance variables, and the concepts of polymorphism and dynamic binding.
Message Syntax
Sending Messages to nil
The Receiver’s Instance Variables
Polymorphism
Dynamic Binding
To get an object to do something, you send it a message telling it to apply a method. In Objective-C, message expressions are enclosed in brackets:
[receiver message] |
The receiver is an object, and the message tells it what to do. In source code, the message is simply the name of a method and any arguments that are passed to it. When a message is sent, the runtime system selects the appropriate method from the receiver’s repertoire and invokes it.
For example, this message tells the myRect object to perform its display method, which causes the rectangle to display itself:
[myRect display]; |
Methods can also take arguments. The imaginary message below tells myRect to set its location within the window to coordinates (30.0, 50.0):
[myRect setOrigin:30.0 :50.0]; |
Here the method name, setOrigin::, has two colons, one for each of its arguments. The arguments are inserted after the colons. This method name uses unlabeled arguments. Unlabeled arguments make it difficult to determine the kind and purpose of a method’s arguments. Instead, method names should include labels describing each of their arguments. Argument labels precede each colon in the method name. The setWidth:height: method, for example, makes the purpose of its two arguments clear:
[myRect setWidth:10.0 height:15.0]; |
Methods that take a variable number of arguments are also possible, though they’re somewhat rare. Extra arguments are separated by commas after the end of the method name. (Unlike colons, the commas aren’t considered part of the name.) In the following example, the imaginary makeGroup: method is passed one required argument (group) and three that are optional:
[receiver makeGroup:group, memberOne, memberTwo, memberThree]; |
Like standard C functions, methods can return values. The following example sets the variable isFilled to YES if myRect is drawn as a solid rectangle, or NO if it’s drawn in outline form only.
BOOL isFilled; |
isFilled = [myRect isFilled]; |
Note that a variable and a method can have the same name.
One message expression can be nested inside another. Here, the color of one rectangle is set to the color of another:
[myRect setPrimaryColor:[otherRect primaryColor]]; |
Objective-C 2.0 also provides a dot (.) operator that offers a compact and convenient syntax for invoking an object’s accessor methods. This is typically used in conjunction with the declared properties feature (see “Properties”), and is described in “The Dot Syntax.”
In Objective-C, it is valid to send a message to nil—it simply has no effect at runtime. There are several patterns in Cocoa that take advantage of this fact. The value returned from a message to nil may also be valid:
If the method returns an object, any pointer type, any integer scalar of size less than or equal to sizeof(void*), a float, a double, a long double, or a long long, then a message sent to nil returns 0.
If the method returns a struct, as defined by the Mac OS X ABI Function Call Guide to be returned in registers, then a message sent to nil returns 0.0 for every field in the data structure. Other struct data types will not be filled with zeros.
If the method returns anything other than the aforementioned value types the return value of a message sent to nil is undefined.
The following code fragment illustrates valid use of sending a message to nil.
id anObjectMaybeNil = nil; |
// this is valid |
if ([anObjectMaybeNil methodThatReturnsADouble] == 0.0) |
{ |
// implementation continues... |
} |
Note: The behavior of sending messages to nil changed with Mac OS X v10.5; for the behavior of previous versions, see prior documentation.
A method has automatic access to the receiving object’s instance variables. You don’t need to pass them to the method as arguments. For example, the primaryColor method illustrated above takes no arguments, yet it can find the primary color for otherRect and return it. Every method assumes the receiver and its instance variables, without having to declare them as arguments.
This convention simplifies Objective-C source code. It also supports the way object-oriented programmers think about objects and messages. Messages are sent to receivers much as letters are delivered to your home. Message arguments bring information from the outside to the receiver; they don’t need to bring the receiver to itself.
A method has automatic access only to the receiver’s instance variables. If it requires information about a variable stored in another object, it must send a message to the object asking it to reveal the contents of the variable. The primaryColor and isFilled methods shown above are used for just this purpose.
See “Defining a Class” for more information on referring to instance variables.
As the examples above illustrate, messages in Objective-C appear in the same syntactic positions as function calls in standard C. But, because methods “belong to” an object, messages behave differently than function calls.
In particular, an object can be operated on by only those methods that were defined for it. It can’t confuse them with methods defined for other kinds of objects, even if another object has a method with the same name. This means that two objects can respond differently to the same message. For example, each kind of object sent a display message could display itself in a unique way. A Circle and a Rectangle would respond differently to identical instructions to track the cursor.
This feature, referred to as polymorphism, plays a significant role in the design of object-oriented programs. Together with dynamic binding, it permits you to write code that might apply to any number of different kinds of objects, without you having to choose at the time you write the code what kinds of objects they might be. They might even be objects that will be developed later, by other programmers working on other projects. If you write code that sends a display message to an id variable, any object that has a display method is a potential receiver.
A crucial difference between function calls and messages is that a function and its arguments are joined together in the compiled code, but a message and a receiving object aren’t united until the program is running and the message is sent. Therefore, the exact method that’s invoked to respond to a message can only be determined at runtime, not when the code is compiled.
The precise method that a message invokes depends on the receiver. Different receivers may have different method implementations for the same method name (polymorphism). For the compiler to find the right method implementation for a message, it would have to know what kind of object the receiver is—what class it belongs to. This is information the receiver is able to reveal at runtime when it receives a message (dynamic typing), but it’s not available from the type declarations found in source code.
The selection of a method implementation happens at runtime. When a message is sent, a runtime messaging routine looks at the receiver and at the method named in the message. It locates the receiver’s implementation of a method matching the name, “calls” the method, and passes it a pointer to the receiver’s instance variables. (For more on this routine, see “How Messaging Works.”)
The method name in a message thus serves to “select” a method implementation. For this reason, method names in messages are often referred to as selectors.
This dynamic binding of methods to messages works hand-in-hand with polymorphism to give object-oriented programming much of its flexibility and power. Since each object can have its own version of a method, a program can achieve a variety of results, not by varying the message itself, but by varying just the object that receives the message. This can be done as the program runs; receivers can be decided “on the fly” and can be made dependent on external factors such as user actions.
When executing code based upon the Application Kit, for example, users determine which objects receive messages from menu commands like Cut, Copy, and Paste. The message goes to whatever object controls the current selection. An object that displays text would react to a copy message differently from an object that displays scanned images. An object that represents a set of shapes would respond differently from a Rectangle. Since messages don’t select methods (methods aren’t bound to messages) until runtime, these differences are isolated in the methods that respond to the message. The code that sends the message doesn’t have to be concerned with them; it doesn’t even have to enumerate the possibilities. Each application can invent its own objects that respond in their own way to copy messages.
Objective-C takes dynamic binding one step further and allows even the message that’s sent (the method selector) to be a variable that’s determined at runtime. This is discussed in the section “How Messaging Works.”
Last updated: 2008-02-05