Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSInvocation and super

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

Is there another way to accomplish this?

like image 899
pepsi Avatar asked Aug 20 '26 13:08

pepsi


1 Answers

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.

like image 179
Jon Hess Avatar answered Aug 22 '26 07:08

Jon Hess



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!