【问题标题】:Function Pointers in Objective CObjective C 中的函数指针
【发布时间】:2010-12-19 03:33:06
【问题描述】:

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

【问题讨论】:

    标签: c++ objective-c function pointers


    【解决方案1】:

    通常,您需要两条信息才能回调到 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 之间的小粘合类。粘合类的实例可以包含必要的配置或根据传入的回调数据在选择器之间切换所需的逻辑。

    【讨论】:

    • 嗯...好提示。我想这和使用目标/动作是一样的。基本上,你必须告诉那个愚蠢的回调函数只是与谁交谈。如果我正在使用一个我没有源代码的 API,那将会很有用。实际上,我只是用我自己的代码来实现整个设置。不过,非常感谢。
    • 它类似于目标/动作,但并不完全相同。如果编写纯 C,上下文将是 malloc 的内存块,其中包含回调中所需的任何状态。这几乎正​​是这里发生的事情,但您只是将回调转发到 Objective-C...
    • 使用 Cocoa 开发时,NSInvocation 可以是胶水类:developer.apple.com/mac/library/documentation/cocoa/reference/…
    • 您可以使用 NSInvocation,但它很快就会变得混乱,并且几乎总是会破坏编译器进行类型检查的大部分能力。粘合类编写起来很简单,以后可以轻松扩展,并最大限度地提高编译器仔细检查代码的能力。
    【解决方案2】:

    您可以使用 @selectorSEL 类型来做到这一点:

    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().. .

    如果这听起来不必要地复杂,那是因为它确实如此。 :-)

    【讨论】:

    • 第二个代码 sn-p 确实按照要求提供了 C 风格的函数指针。不过,细节可能有点棘手。
    • 这是一个不错的选择,但我只想传递一个指向函数的指针。也不是指向对象的指针。否则,我只会使用选择器。
    • 通常,IMP 和 Selector 都不会帮助您,因为您还需要引用您需要回调的实例......
    • 或者你可以使用 libffi 以可移植的方式创建一个闭包....但是你会遇到一些与安全相关的令人讨厌的问题(代码必须在标记为可执行的页面上,这并非所有操作系统都支持)。
    • 另外,为了大家好,这个问题主要是我自己无知的问题。我有一些想法,我可以在 Obj-C 中创建一个指向对象方法的函数指针。好吧,那当然是错的。 Obj-C 方法没有绝对地址。 obj-c 方法地址是在运行时确定的。
    【解决方案3】:

    您可以创建一个 C++ 类作为包装器。并从该类中调用目标 C 消息。请记住将您的文件设为 yoursource.mm 而不是 yoursource.cpp

    【讨论】:

      【解决方案4】:

      您可以使用 instanceMethodForSelector: 来执行此操作,请参阅 Apple NSObject documentation 了解更多信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-07
        • 1970-01-01
        • 1970-01-01
        • 2011-05-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多