你有几个选择。
使用块
您可以使用块来传达您的回调工作。这可能是最简单的解决方案,因为它允许您调用代码而无需将任何参数传递给回调“函数”。块在 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 方法调用是编译器的标准函数调用约定,可能或者cdecl
amd64 调用约定,可变参数调用约定并不总是相同的)。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
,您需要分别跟踪要调用的函数和调用它的对象。