用块包装c回调(__bridge_transfer和blocks)

时间:2012-07-31 10:38:39

标签: objective-c ios5 automatic-ref-counting objective-c-blocks

我正在为标准C API编写一个Obj-C包装器。我想用块替换C回调。

设想一个C API:

void my_async_function(void (* callback)(void *), void *udata);

Obj-C包装器如下所示:

- (void)myAsyncFunction:(dispatch_block_t)block
{
    void *udata = (__bridge_retained void *)block;
    my_async_function(my_callback, udata);
}

void my_callback(void *udata)
{
    dispatch_block_t block = (__bridge_transfer dispatch_block_t)udata;
    block();
}

__bridge_retained__bridge_transfer在许多情况下运作良好,但在块上,它们会导致非常奇怪的行为。

myAsyncFunction的汇编代码:根本没有保留(Xcode 4.4,ARC,O3)。

非常奇怪的是,以下核心生成了objc_retainBlock,这是我对myAsyncFunction所期望的:

void *a_global_var;

- (void)myAsyncFunction2:(dispatch_block_t)block
{
    void *udata = (__bridge_retained void *)block;
    a_global_var = udata;
    my_async_function(my_callback, udata);
}

我们可以称这是编译器的错误吗? 如果没有,编译器遵循什么规则?

类似主题:

1 个答案:

答案 0 :(得分:2)

尝试:

- (void)myAsyncFunction:(dispatch_block_t)block
{
    void *udata = (__bridge_transfer void *) [block copy];
    my_async_function(my_callback, udata);
}

void my_callback(void *udata)
{
    // however, see the comment in the last paragraph
    dispatch_block_t block = (__bridge_transfer dispatch_block_t)udata;
    block();
}

通常,当您将块指针指定给位置where it could outlive the block structure it references时,编译器会在Block_copy调用中进行合成。

然而,编译器在将它传递给C api之后无法知道void *会发生什么,并且无论如何你都会覆盖编译器认为应该对__bridge_retained调用应该做的任何事情。 。存储参考时保留一个块是不够的。

此外,即使进行了此更改,您的回调也必须只调用一次,因为它负责释放块。如果它永远不会被调用,你将泄漏块。如果它不止一次被调用,你就会崩溃。所以你可能想让你的包装类的实例负责管理块的内存,除非C api允许你提供一个清理函数,你可以使用它来释放块。