Objective c 使NSInvocation调用特定的IMP

Objective c 使NSInvocation调用特定的IMP,objective-c,nsinvocation,imp,Objective C,Nsinvocation,Imp,我正在寻找一种方法,使一个NSInvocation调用一个特定的IMP。默认情况下,它调用它可以找到的“最低的”IMP(即,最近被覆盖的版本),但我正在寻找一种方法,使它从继承链的更高层调用IMP。我要调用的IMP是动态确定的,否则我可以使用super关键字或类似的东西 我的想法是使用-forwardInvocation:机制捕获一条消息(简单且已在工作),然后更改IMP,使其成为既不是超级实现也不是最远后代实现的方法。(硬的) 我发现的唯一一点是,这需要libffi,这使得它与iOS不兼容。理

我正在寻找一种方法,使一个
NSInvocation
调用一个特定的
IMP
。默认情况下,它调用它可以找到的“最低的”
IMP
(即,最近被覆盖的版本),但我正在寻找一种方法,使它从继承链的更高层调用
IMP
。我要调用的
IMP
是动态确定的,否则我可以使用
super
关键字或类似的东西

我的想法是使用
-forwardInvocation:
机制捕获一条消息(简单且已在工作),然后更改
IMP
,使其成为既不是
超级实现也不是最远后代实现的方法。(硬的)

我发现的唯一一点是,这需要libffi,这使得它与iOS不兼容。理想情况下,我希望这是跨平台的

有什么想法吗

免责声明:我只是在试验


尝试@bbum关于蹦床功能的想法

所以我想我已经把事情安排好了;我已经通过
class\u addMethod()
正确添加了以下蹦床,并输入了它:

id dd_trampolineFunction(id self, SEL _cmd, ...) {
    IMP imp = [self retrieveTheProperIMP];
    self = [self retrieveTheProperSelfObject];
    asm(
        "jmp %0\n"
        :
        : "r" (imp)
        );
    return nil; //to shut up the compiler
}
我已经验证了正确的self和正确的IMP在JMP之前都是正确的,并且
\u cmd
参数也正确地输入。(换句话说,我正确地添加了此方法)


然而,有些事情正在发生。我有时会发现自己跳转到一个方法(通常不是正确的方法),使用nil
self
\u cmd
。其他时候,我会在一个没有任何障碍的地方突然崩溃。思想?(我已经很久没有在汇编中做任何事情了…)我正在x86_64上测试这一点。

NSInvocation只是消息发送的对象表示形式。因此,它不能像正常的消息发送那样调用特定的IMP。为了对特定IMP进行调用,您需要编写一个经过IMP调用例程的自定义NSInvocation类,或者必须编写一个实现该行为的蹦床,然后创建一个表示发送给蹦床的消息的调用(也就是说,你基本上不会在很多事情上使用NSInvocation)。

一个未经测试的想法:

可以使用
object\u setClass()
强制选择所需的
IMP
吗?即

- (void)forwardInvocation:(NSInvocation *)invocation {
    id target = [invocation target];
    Class targetClass = classWithTheImpIWant();
    Class originalClass = objc_setClass(target, targetClass);
    [invocation invoke];
    objc_setClass(target, originalClass);
}

考虑到您已经拥有IMP,您只需要一种方法来对所述IMP进行非常原始的方法调用。并且考虑到您愿意使用类似NSInvocation的解决方案,那么您还可以构建一个类似的代理类

如果遇到这种情况,我将创建一个简单的代理类,其中包含要调用的IMP和目标对象(需要设置
self
参数)。然后,我将在汇编中编写一个trampoline函数,它接受第一个参数,假设它是代理类的实例,获取
self
,将其填充到寄存器中,保存参数0,获取IMP和*JMP作为尾部调用

有了蹦床,您就可以将蹦床添加为代理类上希望转发到特定IMP的任何选择器的IMP


要实现任何一种像这样的通用机制,关键是避免与重写堆栈帧有关的任何事情。避免使用C ABI。避免移动参数。

我认为最好的选择是使用libffi。您在上看到iOS的端口了吗?我没有尝试该端口,但我已成功地使用任意参数调用IMP在Mac电脑上


看看JSCocoa,它包括帮助您从
方法
中准备
ffi\u cif
结构的代码,并且它还包含一个应该在iOS上编译的libffi版本。(也没有测试过)

在很久之后添加,以供参考:

您可以使用专用API进行此操作。请将此类别放在方便的地方:

@interface NSInvocation (naughty)
-(void)invokeUsingIMP:(IMP)imp;
@end
瞧,这正是你所期待的。我从迈克·阿什的一篇旧博文中找到了这颗宝石


像这样的私有API技巧对于研究或内部代码非常有用。请记住从appstore绑定的构建中删除它。

您可能应该看看我们如何在中的对象实例上快速实现特定方法

基本上,你可以为一个类的所有对象调用这个方法,这样当它被调用时,就可以在字典中查找目标对象必须调用的实现,如果找不到,就返回到原来的实现


您也可以使用private invokeWithImp:方法,但如果您打算将应用程序提交到应用商店,则不建议这样做。

您可以使用新选择器下的class_addMethod将IMP添加到类中,并调用该选择器


但是,临时方法无法删除。

+1这是一个非常有趣的想法。不幸的是,如果目标
IMP
调用任何其他方法,它们将转到与目标
IMP
相同级别的版本,而不是最远的后代。更不用说如果您继续运行,将对任何并发对象造成彻底的破坏我用蹦床函数更新了我的问题(这不是很有效)。如果你能看一下的话,我会很感激。你不能用C来做这件事。它需要汇编,你需要玩一点游戏,获得正确的IMP。