如何使用来自ObjC类的std :: function回调在C ++中进行异步工作?

时间:2015-06-16 10:37:57

标签: ios c++11 asynchronous objective-c++

大图

我有一个C ++库,可以进行包括网络在内的异步工作。它有一个特定于Darwin的后端,它使用Grand Central Dispatch的C API将工作委托给其他线程。现在我想通过一个薄层的ObjC ++从一个用Swift编写的新iOS应用程序中使用该库。

我在OS X 10.10上使用Xcode 6.3.2。

在这个最小的例子中,我重新创建了上述架构。问题是启动异步操作的ObjC类实例在某种程度上是“#34;破坏"当操作通过std :: function回调返回时。如果std :: function声明为[&]而不是[=],则只发生 。我不能使用后者,因为它不支持"真正的" C ++代码。

调用ObjC ++的Swift代码如下所示:

class MyViewController : UIViewController {
    var test = ObjectiveTest()

    override func viewDidAppear(animated: Bool) {
        test.testAsyncWork("some data", withHandler: { (result: Int32) -> Void in
            println("Work result: \(result)")
        })
    }
}

如果我注释掉该代码,则显示视图控制器并保持可见,因此它不应该杀死ObjectiveTest实例。这就是ObjC ++胶水层:

@interface ObjectiveTest () {
    __block Test *test;
    __block int _a;
}
@end

@implementation ObjectiveTest
- (id)init
{
    self = [super init];
    if (self) {
        test = new Test();
        _a = 42;
        if (!test)
            self = nil;
    }
    return self;
}

- (void)deinit
{
    delete test;
}

- (void)testAsyncWork:(NSString *)someData withHandler:(WorkBlock)handler
{
    _a++;
    NSLog(@"_a = %i", _a); // valid
    NSLog(@"handler = %@", handler); // valid
    test->testAsyncWork(someData.UTF8String, [&](int result) {
        _a++;
        NSLog(@"result = %i", result); // Expected: "result = 666" - valid
        NSLog(@"_a = %i", _a); // Expected: "_a = 44" - invalid
        NSLog(@"handler = %@", handler); // invalid, crashes here
    });
}
@end

最后,执行"工作":

的C ++方法
void Test::testAsyncWork(std::string someData, std::function<Handler> handler) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        printf("Data received: %s, calling handler on other thread\n", someData.c_str());
        sleep(1); // This is hard work!

        dispatch_async(dispatch_get_main_queue(), ^{
            printf("Hello, main thread here, calling back now!\n");
            handler(666); // into ObjectiveTest
        });
    });
}

观察

正如我所说,如果我将[=]用于std :: function,这不会中断。

尽管使用_a声明,但ObjectiveTest的ivar __block似乎在回调函数中具有随机值。尝试访问(打印/调用)调用回Swift代码的块handler时,程序崩溃。 Xcode显示如下:

handler WorkBlock & error: summary string parsing error 0xbffa50cc
&handler    __block_literal_generic *   0x7c000000  0x0ae08500
__isa   void *  NULL    0x00000000
__flags int 0   0
__reserved  int 2080876705  2080876705
__FuncPtr   void (*)(int)   0x7c000000  0x7c000000
__descriptor    __block_descriptor *    0x7c085b10  0x7c085b10

由此,我得到的印象是ObjectiveTest实例在进程的某个地方中断,但由于它保存在MyViewController中,我不知道这是怎么发生的。我可能会错过其他什么吗?

1 个答案:

答案 0 :(得分:3)

[&]通过引用捕获变量。如果原始变量在任务完成之前到期,则捕获的引用将悬空。

由于异步调用可能是异步完成 1 ,所以基本上保证了悬空引用。

进行异步调用时,您几乎总是希望按值捕获。您甚至可能希望列出正在捕获显式的内容,以便了解您要引入的依赖项。 (多线程代码很难,没有隐藏/隐式依赖)

[&]捕获的唯一有效用途是当您创建一个&#34;访问者&#34;键入对象以传递给将使用lambda的函数,然后在它返回之前丢弃它及其所有副本。除此之外的任何事情你都应该通过价值来捕捉,或者非常仔细地挑选你通过引用捕获的内容,并证明生命周期问题都已被涵盖。

1 可能希望[&]的异步方法示例是在UI泵线程上运行&#34;&#34;异步调用,您希望工作线程停止进度,直到结果从UI线程返回。在那里,您[&],并在离开当前范围之前阻止返回的std::future(或等效的)。