10

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

我的想法是使用该-forwardInvocation:机制来捕获消息(简单且已经工作),然后更改IMP它,使其转到既不是super实现也不是最远后代实现的方法。(难的)

我发现唯一可以远程关闭的是AspectObjectiveC,但这需要 libffi,这使得它与 iOS 不兼容。理想情况下,我希望这是跨平台的。

有任何想法吗?

免责声明:我只是在尝试


尝试@bbum 的蹦床功能想法

所以我认为我已经完成了大部分工作;我有以下通过 正确添加的蹦床class_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 之前都是正确的,并且_cmd参数也正确输入。(换句话说,我正确地添加了这个方法)。

然而,事情正在发生。我有时会发现自己跳到一个方法(通常不是正确的方法)与一个 nilself_cmd. 其他时候,我会用 EXC_BAD_ACCESS 在不知名的地方崩溃。想法?(自从我在汇编中做任何事情以来已经很长时间了......)我正在 x86_64 上测试它。

4

7 回答 7

4

NSInvocation 只是消息发送的对象表示。因此,它不能调用特定的 IMP,就像发送普通消息一样。为了让调用调用特定的 IMP,您要么需要编写一个自定义 NSInvocation 类来通过 IMP 调用例程,要么您必须编写一个实现该行为的蹦床,然后创建一个表示给蹦床的消息(即,您基本上不会在任何事情上使用 NSInvocation)。

于 2011-02-19T06:17:41.283 回答
4

事后很久才补充,供参考:

您可以使用私有 API 来完成。将此类别放在方便的地方:

@interface NSInvocation (naughty)
-(void)invokeUsingIMP:(IMP)imp;
@end

瞧,它完全符合您的期望。我从 Mike Ash 的一篇旧博文中挖掘出这颗宝石。

像这样的私有 API 技巧非常适合研究或内部代码。只要记住从你的应用商店绑定的构建中删除它。

于 2013-02-05T22:24:30.527 回答
3

一个未经验证的想法:

你可以object_setClass()用来强制选择IMP你想要的吗?那就是……</p>

- (void)forwardInvocation:(NSInvocation *)invocation {
    id target = [invocation target];
    Class targetClass = classWithTheImpIWant();
    Class originalClass = objc_setClass(target, targetClass);
    [invocation invoke];
    objc_setClass(target, originalClass);
}
于 2011-02-19T06:44:56.513 回答
3

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

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

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

要实现这样的任何类型的通用机制,关键是要避免与重写堆栈帧有关的任何事情。避免使用 C ABI。避免移动争论。

于 2011-02-19T08:10:03.347 回答
2

我认为你最好的选择是使用 libffi。您是否在https://github.com/landonf/libffi-ios看到了 iOS 端口?我还没有尝试过这个端口,但我已经成功地在 Mac 上使用任意参数调用了 IMP。

看看 JSCocoa https://github.com/parmanoir/jscocoa,它包含帮助您从 a 准备ffi_cif结构的代码Method,它还包含应该在 iOS 上编译的 libffi 版本。(也没有测试过)

于 2011-02-19T11:23:24.763 回答
0

您可能应该看看我们如何在https://github.com/tuenti/TMInstanceMethodSwizzler中的对象实例上调配特定方法的实现

基本上,你为一个类的所有对象调整方法,所以当它被调用时,它会在字典中查找必须为目标对象调用的实现,如果没有找到则回退到原始实现。

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

于 2014-03-31T08:32:20.743 回答
0

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

虽然不能删除临时方法。

于 2014-10-08T14:20:24.920 回答