2

I am working on xcode8 swift 3.0 project. It needs to access a C++ library which will need a callback function to send data back to swift caller asynchronizely. The callback does work if it is called right inside the RegisterCallBack function. However, if it crashes if call it outside the RegisterCallBack function.

in my swift file ViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()
    var closure: () -> Void = testfunc;
    RegisterCallBack(closure)

    run_swiftfunc()
}
func testfunc(){
    print("test func in view contriller ");
}
....

// in my wrapper.h file

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

// in my wrapper.cpp file

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
} 
...
}

//log print: RegisterCallBack function pointer 0x1300cd30

run_swiftfunc

function pointer 0x1300cd30

test func in view contriller

run_swiftfunc

function pointer 0x1300cd30

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

The swiftfunc address is same both are 0x1300cd30. How to preserve the swiftfunc block?

Hannah Zhang
  • 489
  • 5
  • 5
  • Possible duplicate of [Interacting with C++ classes from Swift](http://stackoverflow.com/questions/35229149/interacting-with-c-classes-from-swift) – CodeBender Jul 07 '16 at 04:11

2 Answers2

1

I found out that the block is only stack, so only works in the scope of the function. I end up use Block_copy to avoid the problem. Now I can use asynchronous callback function to call swift functions in c++

// in my wrapper.cpp file

 #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();   
    } 
 ...
 }
Hannah Zhang
  • 489
  • 5
  • 5
0

Make the closure __strong in the cpp file.

//
//  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

Then I include TestCallbacks.h in the bridging header..

Then in swift I do:

override func viewDidLoad() {
    super.viewDidLoad()
    let closure: @convention(block) () -> Void = self.testfunc
    RegisterCallBack(closure)
    run_swiftfunc()
}

func testfunc(){
    print("test func in view contriller ");
}

I advise that when you are finished using it, you nil the reference just in case.

The other option is to use the Objective-C runtime:

#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
Brandon
  • 22,723
  • 11
  • 93
  • 186