Forwarding Messages

When an object is sent a message for which it has no method, the runtime system gives it another chance to handle the call before giving up. If the object supports a -forward :: method, the runtime calls this method, passing it information about the unhandled call. The return value from the forwarded call is propagated back to the original caller of the method.

Both Object and NSObject provide a -forward:: method, so all objects you are likely to encounter will behave as described in this section. In each case, the default behavior, implemented by the root class method, is to exit the program when an unhandled call is forwarded. Your classes can override this method to provide alternative behavior; common choices are to absorb the error or to redirect (forward) the unhandled message to another object, which is called a delegate .

Object Forwarding

The -forward:: method takes a selector and a second parameter that stores information about the parameters passed to the original (failed) method call.

The method supplied by GNU’s Object sets off the following train of events:

  1. -forward:: calls -doesNotRecognize : on self, passing the selector for the original method call.

  2. -doesNotRecognize: in turn calls -error : passing a format string and the name of the original method call.

  3. -error: logs an error message and aborts the program, unless you have overridden this method or installed an alternative error handler. Section 1.8 discusses this more.

If you want to just ignore messages that your class does not handle, you can override -forward:: to return NULL. To implement true forwarding for a class, override the -forward:: method in the following way:

1 -(retval_t)forward:(SEL)sel :(arglist_t)args {
2   if ([myDelegate respondsTo:sel])
3     return [myDelegate performv:sel :args]
4   else
5     return [super forward:sel :args];
6 }

Line 2. Check that the delegate actually handles the call. The -respondsTo: method does not itself take forwarding into account; if the delegate might itself have another delegate, you could skip this test and just execute line 3.

Line 3. Return the result of the forwarded call. Because you pass the parameters straight through, you don’t need to know the details of the arglist_t type. Since retval_t is defined as a pointer type, you should not forward calls that return floating point numbers, structures, or other incompatible types.

Line 4. If you want to ignore any messages your delegate doesn’t handle, you can skip this line and line 5.

Line 5. If your parent class may have an alternative way to forward the message, pass the message using the super keyword.

NSObject Forwarding

In Cocoa, the -forward:: method is private to NSObject, so you shouldn’t override it. The method sets off the following chain of events:

  1. -forward:: calls -methodSignatureForSelector : on self, passing the selector for the original method call. You will have to override this method to provide an NSMethodSignature instance describing the signature of the method your forwarder will call.

    Note

    If you don’t override -methodSignatureForSelector:, the inherited version will return nil. This will cause later stages of forwarding to raise an exception.

  2. -forward:: uses the signature returned from the previous call to construct an NSInvocation instance, and passes that to -forwardInvocation:.

  3. The -forwardInvocation: method is the one you override to customize forwarding behavior. The version provided by NSObject simply calls -doesNotRecognizeSelector :.

  4. The NSObject version of -doesNotRecognizeSelector: raises an NSInvalidArgument exception.

  5. If your program doesn’t handle the exception it will exit with return value 255. Section 1.8 describes how to handle exceptions.

To forward a message you must override two NSObject methods. If you want to just absorb the message, the process is simple, and different enough from the general case to merit a description here:

1 -(NSMethodSignature*)
2 methodSignatureForSelector:(SEL)sel {
3   return 
4     [NSObject
5       instanceMethodSignatureForSelector:
6         @selector(self)];
7 }
8
9 -(void)forwardInvocation:(NSInvocation*)inv { }

Line 3. You must return a valid NSMethodSignature instance, even though the rest of your forwarding code will ignore it. You can be sure this example will succeed, because NSObject will always be present, with its -self method.

Line 9. Override this method to ignore its parameter and do nothing.

If you want to forward an unhandled method call to another object, you have to attend to more details. Again, you first override -methodSignatureForSelector: to return an object that encapsulates information about the method’s interface:

 1 -(NSMethodSignature*)
 2 methodSignatureForSelector:(SEL)sel {
 3   NSMethodSignature* sig =
 4     [[self class]
 5       instanceMethodSignatureForSelector:sel];
 6   if (sig =  = nil) 
 7     sig = [myDelegate 
 8       methodSignatureForSelector:sel];
 9   if (sig =  = nil)
10     sig = [Unused
11      instanceMethodSignatureForSelector:
12        @selector(unused)];
13   return 
               sig;
14 }

Line 2. When this method is called for the purposes of forwarding, the parameter will be a selector for a method the receiver doesn’t handle.

Line 3. Check to see if your object already handles the method. You do this because this method may be called for reasons other than preparing to forward a call your object doesn’t handle. Since calling the method you are overriding will cause an infinite loop, you have to call the class instead.

Line 6. If your object does not handle the message, sig will be nil at this point and you can ask your delegate about the method’s signature.

Line 9. If sig is still nil at this point, your delegate is not equipped to handle the forwarded message. (This would certainly be a programming error.) If you return nil your program will crash. Your best recovery strategy is to have a special-purpose class with a single method, unused anywhere in your program. Returning its method signature lets your -forwardInvocation: method detect that the delegate won’t handle it, and instead ignore it.

Next you override -forwardInvocation: to redirect the call:

1 -(void)forwardInvocation:(NSInvocation*)inv {
2   if ([myDelegate respondsToSelector:
3                   [inv selector]])
4     [inv invokeWithTarget:myDelegate];
5   else
6     [super forwardInvocation:inv];
7 }

Line 1. The parameter contains information about the method call: the selector, parameter values and types, and return type.

Line 2. Check that the delegate actually handles the call.

Line 4. If the delegate will handle the call, forward the message.

Line 5. If you intend to just ignore messages that the delegate won’t handle, you don’t need this line or line 6.

Line 6. If your parent class may have an alternative way to forward the message, pass the invocation object to it using the super keyword.

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

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