pepsi
pepsi

Reputation: 6865

NSInvocation and super

Why can't super be set as the target of an NSInvocation?

Is there another way to accomplish this?

Upvotes: 2

Views: 2118

Answers (4)

Moritz
Moritz

Reputation: 1640

if you have an object x and want to call the superclass test selector, one way is to create a temporary method with the super implementation and call it:

Method method = class_getInstanceMethod([x superclass], @selector(test));
class_addMethod([x class], @selector(test_super), method_getImplementation(method), method_getTypeEncoding(method));
inv = [NSInvocation invocationWithMethodSignature:[x methodSignatureForSelector:@selector(test)]];
[inv setTarget:x];
[inv setSelector:@selector(test_super)];
[inv invoke];

Upvotes: 0

Jon Hess
Jon Hess

Reputation: 14247

Although it looks a lot like self, super is not a variable. It is a keyword. For example, this is a syntax error:

- (void)log {
    NSLog(@"%@", super);
}

The 'super' keyword can only be used as the receiver of a message, and in that one case means to avoid the normal polymorphic dispatch and call the method that belongs to the super class of the code in question.

If you have something like this:

@interface Vehicle : NSObject
@end

@interface Car : Vehicle
@end

@implementation Vehicle
- (void)log {
    NSLog(@"-[Vehicle log] invoked on an instance of %@", NSStringFromClass([self class]));
}
@end


@implementation Car
- (void)log {
    NSLog(@"-[Vehicle log] invoked on an instance of %@", NSStringFromClass([self class]));
}
@end

Then here's one way you could get at -[Vehicle log] when the receiver was an instance of Car.

@implementation Car

- (void)log {
    NSLog(@"-[Vehicle log] invoked on an instance of %@", NSStringFromClass([self class]));
}

- (void)runVehiclesLog {
    [super log];
}

- (void)runInvocationThatTargetsVehicle {
    SEL selector = @selector(runVehiclesLog);
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
    [invocation setTarget:self];
    [invocation setSelector:selector];
    [invocation invoke];
}
@end

If you can't edit the class but still need to do this, then instead of using NSInvocation you could use +[NSObject instanceMethodForSelector:] like this:

typedef void (*MyVoidMethodWithNoArgs)(id receiver, SEL selector);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *car = [[Car alloc] init];
        MyVoidMethodWithNoArgs imp = (MyVoidMethodWithNoArgs)[Vehicle instanceMethodForSelector:@selector(log)];
        imp(car, @selector(log));
    }
    return 0;
}

In that second case, you're also avoiding dynamic dispatch, but are dropping down to getting pointers to the c functions that implement the methods you've defined above. It's very important to cast the result of instanceMethodForSelector: to a function with the correct prototype before calling it. Also, the selector argument isn't choosing a method, but is instead populating the second hidden argument to all objective C functions, the selector being invoked. If you passed a different selector in the call to imp, the code would still run but would be violating convention.

Upvotes: 6

Dave DeLong
Dave DeLong

Reputation: 243156

Yes there is, but it uses private API and should never be used in a shipping app.

A while ago, Mike Ash discovered a private method on NSInvocation called -invokeUsingIMP:. He has used this in some of the things he has posted online which let him accomplish some really neat tricks.

You can use this private -invokeUsingIMP: method to call super, by figuring out what the IMP is that corresponds to a call to super, and then passing that IMP as the argument to -invokeUsingIMP:.

Since this is private API and you shouldn't really be using it, I'm not going to post a code sample demonstrating how to do this.

But yes, it is possible.

Upvotes: 5

Caleb
Caleb

Reputation: 125007

super points to exactly the same object that self does -- try logging the value of both self and super sometime and see for yourself. The difference is that super causes the search for methods to start with the parent class.

So, if you pass super as an object reference (if the compiler even lets you do that), it's basically the same as passing self.

Upvotes: 2

Related Questions