forwardInvocation not being called?
Asked Answered
C

2

19

I'm having trouble getting forwardInvocation to work. For some reason, the Objective-C runtime completely ignores my forwardInvocation: method and throws an unrecognized selector exception.

My test code is as follows:

@interface InvocationTest : NSObject
{
}

+ (void) runTest;

@end


@interface FullClass: NSObject
{
    int value;
}
@property(readwrite,assign) int value;

@end

@implementation FullClass

@synthesize value;

@end


@interface SparseClass: NSObject
{
}

@end

@implementation SparseClass

- (void)forwardInvocation:(NSInvocation *)forwardedInvocation
{
    NSLog(@"ForawrdInvocation called");

    FullClass* proxy = [[[FullClass alloc] init] autorelease];
    proxy.value = 42;
    [forwardedInvocation invokeWithTarget:proxy];
}

@end


@implementation InvocationTest

+ (void) runTest
{
    SparseClass* sparse = [[[SparseClass alloc] init] autorelease];
    NSLog(@"Value = %d", [sparse value]);
}

@end

I'm working off information from the following resources:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105 http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

As far as I can tell, the runtime should be calling forwardInvocation: on the instance of SparseClass when I invoke [sparse value], but it gets completely ignored:

-[SparseClass value]: unrecognized selector sent to instance 0x4b1c4a0 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SparseClass value]: unrecognized selector sent to instance 0x4b1c4a0'

Caswell answered 6/1, 2011 at 23:5 Comment(0)
P
39

You also have to override - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector to get it working.

I guess

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [FullClass instanceMethodSignatureForSelector:aSelector];
}

should be ok.

Prolix answered 6/1, 2011 at 23:19 Comment(1)
Awesome! Worked like a charm.Caswell
C
26

From the NSObject documentation:

Important: To respond to methods that your object does not itself recognize, you must override methodSignatureForSelector: in addition to forwardInvocation:. The mechanism for forwarding messages uses information obtained from methodSignatureForSelector: to create the NSInvocation object to be forwarded. Your overriding method must provide an appropriate method signature for the given selector, either by preformulating one or by asking another object for one.

And, from the runtime documentation:

... if an object forwards any remote messages it receives, it should have a version of methodSignatureForSelector: that can return accurate descriptions of the methods that ultimately respond to the forwarded messages; for example, if an object is able to forward a message to its surrogate, you would implement methodSignatureForSelector: as follows:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector 
{ 
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    } 
    return signature;
}

Note: See Jilouc's answer for the proper implementation of methodSignatureForSelector:.

Cobb answered 6/1, 2011 at 23:20 Comment(1)
I think Apple's Runtime Programming Guide docs fail on this issue. In the "Forwarding" section they mention nothing about needing methodSignatureForSelector: with forwardInvocation:. To find that tidbit you have scroll 3 sections down to find out you need to implement a section method.Endgame

© 2022 - 2024 — McMap. All rights reserved.