提问人:Sam Stewart 提问时间:11/22/2009 最后编辑:Sam Stewart 更新时间:11/22/2009 访问量:12588
目标 C 中的函数指针
Function Pointers in Objective C
问:
快速提问。有谁知道如何获取目标 c 方法的函数指针? 我可以将 C++ 方法声明为函数指针,但这是一个回调方法,因此 C++ 方法需要成为类的一部分,以便它可以访问实例字段。我不知道如何使 C++ 方法成为目标 C 类的一部分。 有什么建议吗?
答:
您可以创建 C++ 类作为包装器。并从该类调用目标 C 消息。只要记住让你的文件 yoursource.mm 而不是你的源.cpp。
你可以用和类型来做到这一点:@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);
根据您的意见进行更新
如果你不想指定对象,在这种情况下,你要求的东西非常不可移植。从本质上讲,你想要的 lambda 表达式不是 C 的功能,尽管它是在 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
因此,我们可以将其复制到“d缓冲区”中,对其进行修补,然后调用以使其可执行。malloc()
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();
只要此函数存在,您可能希望执行此函数,以及何时以及是否应该选择释放它。(无论如何,最好将这种 sketcyness 包装在一个对象中,然后使用普通的 objc refcounting 来控制缓冲区和对 .也许你还想用来分配而不是......[object retain]
[object release]
object
mmap()
malloc()
如果这听起来不必要地复杂,那是因为它确实如此。:-)
评论
有关详细信息,请参阅 Apple NSObject 文档。instanceMethodForSelector:
通常,您需要两条信息才能回调到 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 之间的小胶水类。glue 类的实例可以包含根据传入的回调数据在选择器之间切换所需的配置或逻辑。
评论