Reputation: 6865
Why can't super
be set as the target of an NSInvocation
?
Is there another way to accomplish this?
Upvotes: 2
Views: 2118
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
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
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
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