Question

I'm using the forwardInvocation: feature of objective-c and I need to know what type of argument the method received. In my example I'm passing it an int but getArgumentTypeAtIndex: tells me it's an id instead. Here's a simple example:

@interface Do : NSObject
+ (void) stuff:(int)x;
@end
@implementation Do
+ (NSMethodSignature *) methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature)
        signature = [self methodSignatureForSelector:@selector(forwardInvocation:)];
    return signature;
}

+ (void)forwardInvocation:(NSInvocation *)i
{
    const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
    NSLog(@"%s == %s", argType, @encode(id)); // @ == @
    NSLog(@"%s == %s", argType, @encode(int)); // @ == i
}
@end

Here's how I call it:

[Do stuff:123];

Any idea why I'm not getting id instead of int as the type?

Was it helpful?

Solution

The problem is that you don't have actually have a stuff: method on the class so methodSignatureForSelector: will return nil - it looks like you discovered that and so implemented your own version, but that fails on the super call and so ends up returning the signature of forwardInvocation: - which is not what you want!

To get around this you either need to direct the methodSignatureForSelector: to a class which has the selector, or use a protocol - if a class implements a protocol then it will return the signature for any methods in that protocol even if the methods are not actually implemented by that class.

Here is your sample using a protocol:

@protocol DoProtocol
@optional
+ (void) stuff:(int)x;
@end

@interface Do : NSObject<DoProtocol>
@end

@implementation Do

+ (void)forwardInvocation:(NSInvocation *)i
{
   const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
   NSLog(@"%s == %s", argType, @encode(id)); // @ == @
   NSLog(@"%s == %s", argType, @encode(int)); // @ == i
}

@end

The @optional avoids any compiler warnings for unimplemented methods. The default implementation of methodSignatureForSelector: (from NSObject) will return a valid signature obtained from the protocol, and so forwardInvocation: will be called.

OTHER TIPS

As long as you can get it past the compiler, whatever you pass as an argument will be interpreted as such at runtime - you could declare that a function takes an NSNumber, but it you pass a UITableView to it, it's class will still be a UITableView.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top