从 Objective-C 块创建 IMP

问题描述 投票:0回答:2

据我所知,Objective-C 中的

IMP
类型代表函数指针。有什么方法可以从块指针创建
IMP
吗?谢谢你的想法。

objective-c cocoa osx-snow-leopard objective-c-blocks objective-c-runtime
2个回答
28
投票

自从编写此文以来,iOS 和 Mac OS X 中现在有 API 允许将 Block 直接转换为 IMP。我写了一篇博客文章描述 API (imp_implementationWithBlock())。

IMP imp = imp_implementationWithBlock(^(id _self) {
    // do something here
});
class_addMethod(obj.class, SELToImp, imp, "v@:");

块实际上是一种结构,其中包含一些元数据、对块内包含的代码的引用以及块内捕获的常量复制数据的副本。

因此,不,无法在

IMP
和块引用之间直接映射。

编译对块的调用时,编译器会发出一个蹦床,用于设置堆栈帧,然后跳转到块内的可执行代码。

不过,您可以做的是创建一个像蹦床一样工作的 IMP。对于以下内容,您将需要一个特定的 IMP 来调用每组参数和返回类型。如果您需要使其通用,则需要使用程序集。

为此,我为类的每个实例设置唯一的块实例。如果您想要一个跨所有实例起作用的通用块,请为每个类构建

SEL
-> 块的哈希值。

(1) 使用关联引用机制将块关联为 IMP。比如:

void (^implementingBlock)(id s, SEL _c, int v) = ^(id s, SEL _c, int v) {
     // do something here
}

objc_setAssociatedObject(obj, SELToImp, [implementingBlock copy]));

(2) 实现一个蹦床 IMP,如下所示:

void void_id_sel_intTramp(id self, SEL _cmd, int value) {
    int (^block)(id s, SEL _c, int v) = objc_getAssociatedObject(self, _cmd);
    block(self, _cmd, value);
}

(3) 通过 Objective-C 运行时的 API 将上述蹦床填充到任何选择器中(参见

method_setImplementation
等)

注意事项:

  • 这被输入到 StackOverflow 中。真实的代码会类似,但可能略有不同。

  • 【实现块复制】至关重要;块在堆栈上开始生命(通常)


10
投票

我相信 Mike Ash 在 Landon Fuller 的帮助下,已经解决了一般情况下的这个问题,包括 iOS,这更难。他在 github 上有一个类,它将把一个块变成一个函数指针。

您可能还想发布公告以及一些关于 cocoa-unbound 的有趣的后续讨论。

更新:现在有如上所述的

imp_implementationWithBlock()

© www.soinside.com 2019 - 2024. All rights reserved.