Ios 从C调用Swift闭包时EXC_BAD_访问异常

Ios 从C调用Swift闭包时EXC_BAD_访问异常,ios,c,swift,closures,objective-c-blocks,Ios,C,Swift,Closures,Objective C Blocks,我正试着呼叫C的快速关闭 下面的代码将代表我目前正在进行的工作 首先,在Swift中,我初始化一个静态常量,即应该稍后调用的闭包 然后将此闭包传递给存储块指针的C函数(api\u set\u callback\u block) 一段时间后,调用C函数api\u trigger\u block。此函数应调用Swift闭包。相反,它总是抛出一个运行时错误:当尝试访问cb\u block\u cb()(另请参见下文)时,EXC\u BAD\u ACCESS 通常,这意味着试图访问以前存储的变量的对象被

我正试着呼叫C的快速关闭

下面的代码将代表我目前正在进行的工作

首先,在Swift中,我初始化一个静态常量,即应该稍后调用的闭包

然后将此闭包传递给存储块指针的C函数(
api\u set\u callback\u block

一段时间后,调用C函数
api\u trigger\u block
。此函数应调用Swift闭包。相反,它总是抛出一个运行时错误:当尝试访问
cb\u block\u cb()
(另请参见下文)时,EXC\u BAD\u ACCESS

通常,这意味着试图访问以前存储的变量的对象被释放。然而,当我传递一个静态常数时,我不明白怎么会是这样

我仔细检查了
cb\u block\u cb
在访问它时是否不为空

void(^cb\u block\u cb)(int,int)=NULL;
无效api集合回调块(无效(^cb块)(int,int))
{
如果(cb_块==NULL)
{
puts(“设置回调块时出错:cb_块为空”);
返回;
}
cb_块_cb=cb_块;
}
无效api_触发器_块()
{
if(cb_块_cb==NULL)
{
puts(“错误:触发回调块时:cb_块_cb为空”);
返回;
}
cb_block_cb(3,3);//Swift.Void={(cid,aid)in
打印(“调用回调块!”)
}
}
@UIApplicationMain
类AppDelegate:UIResponder、UIApplicationLegate{
func应用程序(application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.launchOptions键:任意]?)->Bool{
//应用程序启动后自定义的覆盖点。
api\u集合\u回调\u块(CustomClass.callback)
DispatchQueue.main.asyncAfter(截止日期:.now()+7){
api_触发器_块()
}
返回真值
}
// ...
}

谢谢你提出了这个有趣的问题

我看到了一些选择

选项1:

如果您可以控制C API,只需将扩展名从
.C
更改为
.m
,即可使其(部分)成为Objective-C。那么您的Swift代码应该可以与它进行互操作。至少与Swift代码直接交互的部分应为Objective-C。Objective-C部分有望与其余的C部分(在
.C
文件中的部分)互操作

选项2:

围绕C API编写Objective-C包装(在
.m
文件中),并在Swift代码中使用包装。基于您的示例的示例包装器:

// This is where we store the block passed in from Swift
void (^cb_block_cb2)(int, int) = NULL;

// This wrapper will be used in the block passed to C API
void block_wrapper(int i1, int i2) {
    cb_block_cb2(i1, i2);
}

// Obj-C wrapper around C API block setter
// Make this available to Swift, e.g. via bridging header.
void api_set_callback_block2(void (^cb_block)(int, int))
{
    if (cb_block == NULL)
    {
        puts("error: when setting callback block: cb_block is null");
        return;
    }
    cb_block_cb2 = cb_block;
    api_set_callback_block(^(int a, int b){ block_wrapper(a, b);});
}

// Obj-C wrapper around C API block trigger
// Make this available to Swift, e.g. via bridging header.
void api_trigger_block2()
{
    puts("Entered api_trigger_block2()");
    if (cb_block_cb2 == NULL)
    {
        puts("error: when triggering callback block: cb_block_cb2 is null");
        return;
    }
    api_trigger_block();
    puts("Returned from callback in api_trigger_block2()!!!!");
}
从概念上讲,上述选项是相似的。你也许可以根据同样的思路想出一些其他的选择。在这一点上,我不能给出一个很好的技术解释,为什么上面的工作。从实验中我可以看出,Swift函数/闭包不能总是传递给使用C编译器编译并使用兼容块的C函数。但是,如果将相同的函数编译为Objective-C,则可以正常工作。Objective-C文件中定义的块可以很好地传递给C代码

为了记录在案,我还尝试在Swift代码中使用
@objc
@convention(c)
注释来修复这个问题,但没有效果


希望这有帮助。

谢谢你的回答
在这一点上,我无法给出一个很好的技术解释来解释为什么上面的方法会起作用。
我更想知道为什么Swift闭包不能总是传递给C函数,特别是关于我上面的例子。当时,我们只是把C块改成了C函数指针,它的工作原理是一样的,幸运的是,工作得很好。无论如何,我仍然认为您的解决方案对那些无法直接访问底层C代码的人非常有用。顺便说一句,你认为我可以/应该尝试提交一份bug报告吗?事实上,将块更改为函数指针使代码正常工作真的很奇怪,我仔细检查了一下。另一个有趣的观察结果是,代码在崩溃之前似乎循环了一段时间(尝试在
api\u trigger\u block()的开头插入
put()
)。至于提交bug报告,我建议问另一个更简短的问题,显示函数指针工作,而块不工作。如果没有人提出好的解释,我会提交一份bug报告。