【发布时间】:2010-12-19 03:33:06
【问题描述】:
快速提问。有谁知道如何获取目标 c 方法的函数指针? 我可以将 C++ 方法声明为函数指针,但这是一个回调方法,因此 C++ 方法需要成为类的一部分以便它可以访问实例字段。我不知道如何使 C++ 方法成为目标 c 类的一部分。有什么建议吗?
【问题讨论】:
标签: c++ objective-c function pointers
快速提问。有谁知道如何获取目标 c 方法的函数指针? 我可以将 C++ 方法声明为函数指针,但这是一个回调方法,因此 C++ 方法需要成为类的一部分以便它可以访问实例字段。我不知道如何使 C++ 方法成为目标 c 类的一部分。有什么建议吗?
【问题讨论】:
标签: c++ objective-c function pointers
通常,您需要两条信息才能回调到 Objective-C;要调用的方法和调用它的对象。无论是选择器还是 IMP(instanceMethodForSelector: 结果)都不足以提供足够的信息。
大多数回调 API 提供了一个上下文指针,该指针被视为传递给回调的不透明值。这是解决您难题的关键。
即如果你有一个回调函数声明为:
typedef void (*CallBackFuncType)(int something, char *else, void *context);
还有一些使用上述回调函数类型指针的 API:
void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context);
然后你会像这样实现你的回调:
void MyCallbackDude(int a, char *b, void *context) {
[((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b];
}
然后你会调用类似于这样的 API:
MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new];
APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext);
如果您需要在不同的选择器之间切换,我建议创建一个位于回调和 Objective-C API 之间的小粘合类。粘合类的实例可以包含必要的配置或根据传入的回调数据在选择器之间切换所需的逻辑。
【讨论】:
您可以使用 @selector 和 SEL 类型来做到这一点:
SEL sel = @selector(myMethod:);
/* Equivalent to [someObject myMethod:Paramater] */
[someObject performSelector:sel withObject:Parameter]
还有IMP类型...
IMP imp = [someObject methodForSelector:sel];
/* don't remember if this syntax is correct; it's been a while...
* the idea is that it's like a function pointer. */
imp(someObject, sel, Parameter);
根据您的 cmets 更新
如果您不想指定对象,在这种情况下,您要求的是非常不可移植的东西。本质上你想要的不是 C 的特性的 lambda 表达式,尽管它是在 C++0x 中出现的。
所以我的解决方案将是“外面的”、粗略的、不可移植的......
但是...也许您可以通过运行时代码生成来做到这一点...
您可以从在汇编中编写存根函数开始(假设您需要 x86)
push dword 0 ; SEL will go here
push dword 0 ; Object will go here
push dword 0 ; IMP will go here
pop eax ; eax = imp
call eax ; call imp
add esp, 8 ; cleanup stack
ret ; return
这组装成:
0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3
注意指令push dword 0 是字节68 00 00 00 00。我们将在运行时将零填充到指针。
因此,我们可以将其复制到 malloc()'d 缓冲区中,对其进行修补,然后调用 mprotect() 使其可执行。
以下代码仅用于说明目的,我不知道它是否有效。 :-)
/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
0xc3 };
char *buf = malloc(sizeof(code));
SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];
/* Copy template */
memcpy(buf, code, sizeof(code));
/* Patch the "push dword 0" parts with your arguments
* This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));
/* Now here comes the sketchy part...
* Make it executable and turn it into a function pointer. */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;
/* Now, crazy as it sounds, you should be able to do: */
Function();
只要此功能存在,您可能想要执行[object retain],并在何时以及是否应该选择释放它时执行[object release]。 (无论如何,最好将这种粗略包装在一个对象中,然后使用普通的 objc refcounting 来控制缓冲区和对object 的引用。)也许您还想使用mmap() 来分配而不是malloc().. .
如果这听起来不必要地复杂,那是因为它确实如此。 :-)
【讨论】:
您可以创建一个 C++ 类作为包装器。并从该类中调用目标 C 消息。请记住将您的文件设为 yoursource.mm 而不是 yoursource.cpp。
【讨论】:
您可以使用 instanceMethodForSelector: 来执行此操作,请参阅 Apple NSObject documentation 了解更多信息。
【讨论】: