目标C中的功能指针

Sam*_*art 13 c++ pointers function objective-c

快问.有谁知道如何获得目标c方法的函数指针?我可以将C++方法声明为函数指针,但这是一个回调方法,因此C++方法需要成为类的一部分,因为它可以访问实例字段.我不知道如何使C++方法成为客观c类的一部分.有什么建议?

bbu*_*bum 15

通常,您需要两条信息才能回调到Objective-C; 要调用的方法和调用它的对象.既不是选择器也不仅仅是IMP-- instanceMethodForSelector:结果 - 都不是足够的信息.

大多数回调API都提供了一个上下文指针,该指针被视为传递给回调的不透明值.这是你难题的关键.

即如果你有一个声明为的回调函数:

typedef void (*CallBackFuncType)(int something, char *else, void *context);
Run Code Online (Sandbox Code Playgroud)

并且一些API使用所述回调函数类型的指针:

void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context);
Run Code Online (Sandbox Code Playgroud)

然后你将实现你的回调:

void MyCallbackDude(int a, char *b, void *context) {
    [((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b];
}
Run Code Online (Sandbox Code Playgroud)

然后你会将API称为类似于此的东西:

MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new];
APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext);
Run Code Online (Sandbox Code Playgroud)

如果你需要在不同的选择器之间切换,我建议创建一个位于回调和Objective-C API之间的小胶水类.胶水类的实例可以包含基于传入的回调数据在选择器之间切换所必需的配置或逻辑.


asv*_*kau 13

您可以使用@selector以下SEL类型:

SEL sel = @selector(myMethod:);

/* Equivalent to [someObject myMethod:Paramater] */
[someObject performSelector:sel withObject:Parameter]
Run Code Online (Sandbox Code Playgroud)

还有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); 
Run Code Online (Sandbox Code Playgroud)

根据您的评论更新

如果您不想指定对象,在这种情况下,您要求的是非常便携的东西.基本上你想要的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
Run Code Online (Sandbox Code Playgroud)

这集合到:

0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3
Run Code Online (Sandbox Code Playgroud)

注意指令push dword 0是字节68 00 00 00 00.我们将在运行时将零填充到指针.

因此,我们可以将其复制到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();
Run Code Online (Sandbox Code Playgroud)

[object retain]只要此函数存在,您可能希望执行for,以及[object release]何时以及是否应该选择释放它.(无论如何最好将这个sketcyness包装在一个对象中,然后使用普通的objc引用计数来控制缓冲区和引用object.)也许你也想mmap()用来分配而不是malloc()...

如果这听起来不必要复杂,那是因为它.:-)