3

私は標準のCAPIにObj-Cラッパーを書いています。Cコールバックをブロックに置き換えたいと思います。

CAPIを想像してみましょう。

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);
}

それをコンパイラのバグと呼べますか?そうでない場合、コンパイラはどのようなルールに従いますか?

同様のトピック:

4

1 に答える 1

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();
}

通常、コンパイラーは、参照するブロック構造よりも長持ちする可能性Block_copyのある場所にブロックポインターを割り当てると、呼び出しで合成します。

ただし、コンパイラは、void *をCapiに渡した後、void *がどうなるかを知る方法がありません。とにかく、コンパイラが__bridge_retained呼び出しで行うべきだと考えるものは何でもオーバーライドします。参照を保存するときにブロックを保持するだけでは不十分です。

また、この変更があっても、ブロックを解放する責任があるため、コールバックは1回だけ呼び出す必要があります。呼び出されない場合は、ブロックをリークします。複数回呼び出されると、クラッシュします。したがって、C apiでブロックを解放するために使用できるクリーンアップ関数を提供できない限り、ラッパークラスのインスタンスにブロックのメモリの管理を任せることをお勧めします。

于 2012-07-31T12:40:36.567 に答える