使用 EXC_BAD_ACCESS(..) 从 C++ 调用 Swift func

call swift func from c++ with EXC_BAD_ACCESS(...)

本文关键字:C++ 调用 Swift func EXC BAD ACCESS 使用      更新时间:2023-10-16

我正在研究 xcode8 swift 3.0 项目。它需要访问一个C++库,该库需要一个回调函数来异步地将数据发送回 swift 调用者。如果直接在 RegisterCallBack 函数中调用回调,则回调确实有效。但是,如果它在 RegisterCallBack 函数外部调用它,则崩溃。

在我的 swift 文件 ViewController 中.swift

override func viewDidLoad() {
    super.viewDidLoad()
    var closure: () -> Void = testfunc;
    RegisterCallBack(closure)
    run_swiftfunc()
}
func testfunc(){
    print("test func in view contriller ");
}
....

在我的包装器.h文件中

...
void run_swiftfunc();
void RegisterCallBack(void (^closure)());
...

在我的包装器.cpp文件中

extern "C" {
typedef void (^callbackfunc)();
callbackfunc swiftFunc;
void RegisterCallBack(void (^closure)()){
    swiftFunc = closure;
    printf("function pointer 0x%x n", (void*) swiftFunc);
    swiftFunc();  //works well 
}
void run_swiftfunc(){
   printf("function pointer 0x%x n", (void*) swiftFunc);
   swiftFunc();   // fail, EXC_BAD_ACCESS
} 
...
}

日志打印:注册回调函数指针0x1300cd30

run_swiftfunc

函数指针0x1300cd30

View Contriller 中的测试 FUNC

run_swiftfunc

函数指针0x1300cd30

(LLDB) ---->EXC_BAD_ACCESS(code =..

swiftfunc 地址相同,两者都0x1300cd30。如何保存 swiftfunc 块?

我发现该块只是堆栈,因此仅在函数范围内工作。我最终使用Block_copy来避免这个问题。现在我可以使用异步回调函数在 c++ 中调用 swift 函数

在我的包装器.cpp文件中

 #include <Block.h>
 extern "C" {
         typedef void (^callbackfunc)();
         callbackfunc swiftFunc;
         void RegisterCallBack(void (^closure)()){
         swiftFunc = Block_copy(closure);
         printf("function pointer 0x%x n", (void*) swiftFunc);
        swiftFunc();  //works well 
    }
    void run_swiftfunc(){
         printf("function pointer 0x%x n", (void*) swiftFunc);
         swiftFunc();   
    } 
 ...
 }

在 cpp 文件中__strong闭包。

//
//  TestCallbacks.h
//  XIO
//
//  Created by Brandon T on 2016-07-06.
//  Copyright © 2016 XIO. All rights reserved.
//
#ifndef TestCallbacks_h
#define TestCallbacks_h

#ifdef __cplusplus
extern "C" {
#endif
    void run_swiftfunc();
    void RegisterCallBack(void (^closure)());
#ifdef __cplusplus
}
#endif
#endif /* TestCallbacks_h */

//
//  TestCallbacks.mm
//  XIO
//
//  Created by Brandon T on 2016-07-06.
//  Copyright © 2016 XIO. All rights reserved.
//
#include "TestCallbacks.h"
#include <cstdio>
#ifdef __cplusplus
extern "C" {
#endif
    typedef void (^callbackfunc)();
    __strong callbackfunc swiftFunc;
    void RegisterCallBack(void (^closure)()) {
        swiftFunc = closure;
        printf("function pointer %p n", (void*) swiftFunc);
        swiftFunc();
    }
    void run_swiftfunc() {
        printf("function pointer %p n", (void*) swiftFunc);
        swiftFunc();
        swiftFunc = nil;
    }
#ifdef __cplusplus
}
#endif

然后我在桥接标头中包含TestCallbacks.h

然后我迅速地做到:

override func viewDidLoad() {
    super.viewDidLoad()
    let closure: @convention(block) () -> Void = self.testfunc
    RegisterCallBack(closure)
    run_swiftfunc()
}
func testfunc(){
    print("test func in view contriller ");
}

我建议当你用完它时,你取消引用以防万一。

另一种选择是使用 Objective-C 运行时:

#include "TestCallbacks.h"
#include <cstdio>
#import <objc/runtime.h>

#ifdef __cplusplus
extern "C" {
#endif
    typedef void(*callback)(id, SEL)
    callback swiftCallback;
    void RegisterCallBack(void (^closure)()) {
        swiftCallback = (callback)imp_implementationWithBlock(closure);
        printf("function pointer %p n", (void *) swiftCallback);
        swiftCallback(nil, nil);
    }
    void run_swiftfunc() {
        printf("function pointer %p n", (void *) swiftCallback);
        swiftCallback(nil, nil);
        imp_removeBlock((IMP)swiftCallback);
    }
#ifdef __cplusplus
}
#endif