NSInvocation和超级

pep*_*psi 2 cocoa objective-c

为什么不能super设定为目标NSInvocation

还有另一种方法来实现这一目标吗?

Jon*_*ess 6

虽然它看起来很像自我,但超级不是变量.这是一个关键字.例如,这是语法错误:

- (void)log {
    NSLog(@"%@", super);
}
Run Code Online (Sandbox Code Playgroud)

'super'关键字只能用作消息的接收者,并且在一种情况下意味着避免正常的多态调度并调用属于所讨论的代码的超类的方法.

如果您有这样的事情:

@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
Run Code Online (Sandbox Code Playgroud)

然后这是你可以得到的一种方式 - [车辆日志]当接收器是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
Run Code Online (Sandbox Code Playgroud)

如果你不能编辑类但仍然需要这样做,那么你可以使用+ [NSObject instanceMethodForSelector:]而不是使用NSInvocation,如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,您还要避​​免动态调度,但是正在下降到获取指向实现上面定义的方法的c函数的指针.在调用它之前,将instanceMethodForSelector:的结果转换为具有正确原型的函数是非常重要的.此外,selector参数不是选择方法,而是将第二个隐藏参数填充到所有目标C函数,调用选择器.如果在对imp的调用中传递了一个不同的选择器,代码仍会运行,但会违反约定.


Dav*_*ong 5

是的,但它使用私有API,绝不应该在运送应用程序中使用.

不久之前,Mike Ash 发现了一个NSInvocation名为的私有方法-invokeUsingIMP:.他在网上发布的一些东西中使用了这个,这让他可以完成一些非常巧妙的技巧.

您可以使用此私有-invokeUsingIMP:方法调用super,通过确定IMP与调用相对应的内容super,然后将其IMP作为参数传递给-invokeUsingIMP:.

由于这是私有API,你不应该真正使用它,我不打算发布代码示例演示如何执行此操作.

但是,是的,这是可能的.