你有几个选择。
使用块
你可以使用块来传达回调的工作。这可能是最简单的解决方案,因为它允许你调用代码而无需将任何参数传递给回调“函数”。块在C及其所有超集中与Clang一起工作,而Clang++甚至允许块和lambda之间的隐式转换。
#include <dispatch/dispatch.h>
void setup_callback(dispatch_block_t block)
{
dispatch_block_t copy = [block copy];
}
int main()
{
MyClass* instance = [[MyClass alloc] init];
setup_callback(^{
[instance callback_method];
});
}
可能需要在 C++ 端进行一些修改,以接受函数对象(或者只是块,如果更简单的话)而不是函数指针。
由于块创建了闭包,它们非常适合这种工作。
块是 Apple 对 C、C++ 和 Objective-C 的扩展。在此处了解更多信息。
使用 Objective-C 运行时获取要调用的方法的函数指针
使用 Objective-C 运行时来访问您选择器的函数指针。这更加繁琐,需要您跟踪三个变量(要调用方法的对象、要使用的选择器和方法实现),但它在您无法使用 Objective-C 语法的情况下仍然有效。
Objective-C 方法实现是具有此签名的函数指针:
typedef void (*IMP)(id self, SEL _cmd, ...);
这里的self
是您所期望的内容,_cmd
是导致该方法调用的选择器(在所有Objective-C方法中实际上都可以使用_cmd
变量,请尝试),其余部分被视为可变参数。由于可变C函数的调用约定并不总是与Objective-C方法调用的调用约定相匹配(Objective-C方法调用是编译器的标准函数调用约定,可能是cdecl
或amd64调用约定,而可变调用约定并不总是相同的),因此您需要将IMP
变量强制转换为适当的函数签名。 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(instance, @selector(my_callback));
}
在使用函数调用之前,还要注意确保实例不是nil
,因为nil
检查由Objective-C运行时处理。在这种情况下,我们正在绕过它。
跟踪对象和SEL
使用-[NSObject performSelector:]
执行回调。基本上是Objective-C运行时解决方案的简化版本。
void setup_callback(id object, SEL selector)
{
}
int main()
{
MyClass* instance = [[MyClass alloc] init];
setup_callback(instance, @selector(my_callback));
}
将函数调用封装在C ++函数内
我认为这个问题不需要任何示例。创建一个函数,将您的对象类型作为第一个参数接受,并在其上调用所需的方法。与SEL
解决方案类似,您需要单独跟踪要调用的函数和要调用它的对象。
objc_msgSend
来代替实现。 - user102008