Suppose I have this object o which is an instance of
class MyClass :
To invoke the multiply by method of this object, I
could write:
|
int result = [o multiply 6 by 9];
|
In this example, the multiply by method is invoked
with the int arguments 6 and 9, and the
int that is returned is assigned to the variable
result .
A method invocation can also be explained as sending a message to
an object. In the example message being sent, the receiver
of the message is the object o and the arguments of
the message are 6 and 9. The message is completed with a
selector: the selector indicates the intention of the message. In
the example, the selector contains the information to `perform a
multiply by with int arguments 6 and 9,
returning an int '.
When this message is sent, the method indicated by the selector, as
implemented by the receiver, is invoked. Thus, sending a
multiply by message will invoke the multiply
by method. Of course, the argument and return type
information of the selector is significant in the selection of the
method: a method returning a int is different from
one returning a float , a difference that can be also
be discerned in the corresponding selectors.
Resuming, a selector is a name for behavior. Upon sending a message,
this name is bound to an implementation of that behavior, i.e. a
method. Now, why is the selector an important type? Precisely
because it is only a name: it is not bound to a particular object
and has no relation to any implementation.
For example, suppose you have a collection of objects, and you want to
invoke a method on each of the objects. If the collection is an
array, things are easy:
|
int i, n = [array length];
for (i = 0; i < n; i++)
[array[i] doSomething];
|
But, the collection need not be an array; it could be a collection
of any kind, and using a loop like in the example, to walk along
the elements, will not work in the case of a general collection.
Suppose the collection provides the functionality to invoke a
method on each of the objects it contains. In this case, using
C++ as an example, one would pass a function pointer to the method
(member function) to be invoked. However, if that method is a
virtual method, and the objects in the container provide different
implementations, the wrong method will be invoked!
The selector type solves this problem. In the case of an array, the
method to have elements of the array perform some method, could
look like this:
|
void
makeElementsPerform selector a_method
{
int i;
for (i = 0; i < length; i++)
[self[i] perform a_method];
}
|
Now the method invoked on every object will be the proper method,
obeying the rules of method invocations (and the rules of the C++
virtual keyword; in TOM, every method is virtual by definition).
Obviously, this example is only a simple example. The avid reader will
immediately ask `and what about arguments to be passed to the
elements?' This will be discussed in a future TOM Highlight. To
provide a hint: a method invocation can be put in an
Invocation object.
Up: Highlights
|