13

我在objective-c中有ViewController,我的大部分代码都是c++(.mm)。我想从 obj-c(在 c++ 中)设置一些对成员函数的回调,并从 c++ 调用它们。像这样的东西(它非常简化):

@interface MyClass
{ }
-(void)my_callback;
@end

@implementation MyClass

-(void)my_callback
{
   printf("called!\n");
}

-(void)viewDidLoad
{
   // setup_callback( "to my_callback ?" );
}
@end

和:

void setup_callback(void(*func)()) { func(); }

这当然是不正确的。请问我该怎么做?

4

4 回答 4

17

你有几个选择。

使用块

您可以使用块来传达您的回调工作。这可能是最简单的解决方案,因为它允许您调用代码而无需将任何参数传递给回调“函数”。块在 C 及其所有带有 Clang 的超集中工作,Clang++ 甚至允许在块和 lambda 之间进行隐式转换。

#include <dispatch/dispatch.h>

void setup_callback(dispatch_block_t block)
{
    // required to copy the block to the heap, otherwise it's on the stack
    dispatch_block_t copy = [block copy];

    // setup stuff here
    // when you want to call the callback, do as if it was a function pointer:
    // block();
}

int main()
{
    MyClass* instance = [[MyClass alloc] init];

    setup_callback(^{
        [instance callback_method];
    });
}

这可能需要在 C++ 端进行一些修改以接受函子(或者如果更简单,则只接受块)而不是函数指针。

由于块创建了闭包,因此它们对于此类工作非常方便。

Blocks 是 Apple 对 C、C++ 和 Objective-C 的扩展。在这里查看更多关于它们的信息。

使用 Objective-C 运行时获取指向要调用的方法的函数指针

使用 Objective-C 运行时访问选择器的函数指针。这更加繁琐,需要您跟踪三个变量(调用方法的对象、要使用的选择器和方法实现),但即使在您不能使用 Objective-C 的情况下它也确实有效句法。

Objective-C 方法实现是具有此签名的函数指针:

typedef void (*IMP)(id self, SEL _cmd, ...);

self您所期望的_cmd是,导致此方法调用的选择器在哪里(该_cmd变量实际上在所有 Objective-C 方法中都可用,尝试一下),其余的被认为是可变参数。您需要IMP变量转换为正确的函数签名,因为可变参数 C 函数的调用约定并不总是与 Objective-C 方法调用的调用约定相匹配(Objective-C 方法调用是编译器的标准函数调用约定,可能或者cdeclamd64 调用约定,可变参数调用约定并不总是相同的)。Areinterpret_cast将能够做到。

这是我为类似意图整理的一些代码。它使用 C++11 可变参数模板来帮助获得正确的函数签名。

#include <objc/runtime.h>

template<typename TReturnType, typename... TArguments>
auto GetInstanceMethodPointer(Class class, SEL selector) -> TReturnType (*)(id, SEL, TArguments...)
{
    Method m = class_getInstanceMethod(class, selector);
    IMP imp = method_getImplementation(m);
    return reinterpret_cast<TReturnType (*)(id, SEL, TArguments...)>(imp);
}

int main()
{
    MyClass* instance = [[MyClass alloc] init];
    auto foo = GetInstanceMethodPointer<void>(
        [MyClass class],
        @selector(my_callback));
    // foo is a void (*)(id, SEL) function pointer
    foo(instance, @selector(my_callback));
}

还要注意您的实例不在nil使用函数调用之前,因为nil检查是由 Objective-C 运行时处理的。在这种情况下,我们绕过它。

跟踪一个对象和一个SEL

用于-[NSObject performSelector:]执行回调。基本上是 Objective-C 运行时解决方案的更简单版本。

void setup_callback(id object, SEL selector)
{
    // do stuff
    // to execute the callback:
    // [object performSelector:selector];
}

int main()
{
    MyClass* instance = [[MyClass alloc] init];

    setup_callback(instance, @selector(my_callback));
}

将您的调用包装在 C++ 函数中

我认为这个真的不需要任何例子。创建一个接受您的对象类型作为第一个参数的函数,并在其上调用您想要的方法。与解决方案类似SEL,您需要分别跟踪要调用的函数和调用它的对象。

于 2012-08-30T16:31:00.547 回答
2

因为 C++ 和 Obj-C 都是 C 的超集,所以您可以将 C++ 代码回调到 C 函数。要将回调传回您的 Obj-C 对象,只需确保 C 回调参数包含指向原始 Obj-C 对象的指针。

// C++ calls this C function
void exampleCallback(void* classInstance)
{
    // callback arguments include a pointer to the Obj-C object
    [((MYObjCClassType*)classInstance) exampleCallback];
}
于 2012-08-30T16:39:28.073 回答
1

我认为这是不可能的:在 C++ 代码中调用 Objective-C 回调。反过来也是可能的。

当我遇到这个问题时,我所做的是编写一个在 Objective-C 中永远运行的 EVENT_LOOP,并从 C++实例中获取事件。std::vector这样,在 C++ 中,如果您想在 Objective-C 中“调用”回调,只需将事件添加到相应的std::vector.

于 2012-08-30T16:26:51.163 回答
1

您可以在 .mm 文件中自由混合 Obj-C 和 C++ - 它被称为“Objective-C++”。

为了保持 C++ 代码和 Objective-C 代码的隔离,你可以在它们之间创建一个轻量级的外观,这将在 C++ 回调中进行 Objective-C 方法调用(以通常的方式)。

于 2012-08-30T16:31:16.297 回答