5

I'm just studying the "message-forwarding" of Objective-C. I write a test program to verify if I can "swallow" an unrecognized selector at run-time. So I did this:

- (void) forwardInvocation: (NSInvocation *) anInvocation {
    if ([anInvocation selector] == @selector(testMessage)){
       NSLog(@"Unknow message");
    }
    return;
}

But it still throws "unrecognized selector" error at run time. After searching the resolution I know that I need to override method "methodSignatureForSelector:", so I write another proxy class called "Proxy" and the following method:

(NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    if ([Proxy instancesRespondToSelector: selector]) {
        return [Proxy instanceMethodSignatureForSelector: selector];
    }   
    return [super methodSignatureForSelector:selector];
}

But, actually, I don't want to implement such another proxy class to accomplish this method. All I want to do is that ignore this unknown selector. But If I just type this, it does not work:

(NSMethodSignature *)methodSignatureForSelector:(SEL)selector {   
    return [super methodSignatureForSelector:selector];
}

So, I wonder there is any way that can simply "swallow" this error? (Not using exception handler, I wanna a "forwarding"-like way). Thanks!

Jie Wu
  • 51
  • 2

1 Answers1

3

You probably need to generate a signature object, which is a bit tricky:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    NSMethodSignature *sig = [super methodSignatureForSelector:selector];
    if (!sig) {
        if (selector == @selector(testMessage)) {
            // Create a signature for our fake message.
            sig = [NSMethodSignature signatureWithObjCTypes:"@:"];
        }
    }
    return sig;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
   // Do whatever you want with the invocation.
}

The biggest issue seems to be the argument to signatureWithObjCTypes:. See Apple's type encoding documentation. Every method has at least two arguments: id self (type @) and SEL _cmd (type :).

Another way would be to have a dummy method, say - (void)dummy { } and then use sig = [super methodSignatureForSelector:@selector(dummy)]; to get the signature for your faked method.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • It works! Thanks! But one more thing: If I follow your suggestion and try to make such a call: [anInvocation selector] in forwardInvocation: method, error happens: **Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation getArgument:atIndex:]: index (1) out of bounds [-1, 0]'**, Why? – Jie Wu Nov 08 '11 at 07:47
  • 3
    I found [this](http://www.cocoabuilder.com/archive/cocoa/289799-nsproxy-nsinvocation-question.html): *The documentation for [NSMethodSignature signatureWithObjTypes:] is wrong. The first element in the type array is the return typed, not the id or Class type, which in effect left me with an MSMethodSignature instance containing no argument type for the selector parameter.* This probably means that for a method `-(void)foo` you need the type `"v@:"` in the argument to `signatureWithObjCTypes:`. – DarkDust Nov 08 '11 at 07:55