从c ++到目标c的回调

vel*_*yel 13 c++ objective-c callback member-functions

我在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
Run Code Online (Sandbox Code Playgroud)

和:

void setup_callback(void(*func)()) { func(); }
Run Code Online (Sandbox Code Playgroud)

这当然不正确.有什么建议我可以这样做吗?

zne*_*eak 16

你有几个选择.

使用块

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

#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];
    });
}
Run Code Online (Sandbox Code Playgroud)

这可能需要在C++端进行一些重写以接受仿函数(或者只是块,如果它更简单)而不是函数指针.

由于块创建了闭包,因此它们对于这种作品非常方便.

Blocks是C,C++和Objective-C的Apple扩展.在这里查看更多相关信息.

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

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

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

typedef void (*IMP)(id self, SEL _cmd, ...);
Run Code Online (Sandbox Code Playgroud)

哪里self是你所期望,_cmd是导致该方法调用选择(该_cmd变量是在所有的Objective-C的方法,实际可用的,试试吧),其余的被认为是可变参数.您需要IMP变量转换为正确的函数签名,因为可变参数C函数的调用约定并不总是与Objective-C方法调用的调用约定相匹配(Objective-C方法调用是编译器的标准函数调用约定,可能无论是cdecl或AMD64调用约定,和一个可变调用约定并不总是相同).A reinterpret_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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 你甚至不需要实现.您始终可以使用`objc_msgSend`代替实现 (2认同)