5

次の場合 (手動参照カウント):

void (^block)(void) = ^ {
    NSLog(@"wuttup");
}

void (^async_block)(void) = ^ {
    block();
}

dispatch_async(dispatch_get_main_queue(), async_block);

「ブロック」は、スタックからスローされて破棄されるのではなく、コピーされますか?

4

3 に答える 3

8

答えはイエスだと思います。

外側のブロックは非同期にディスパッチされ、ランタイムはこのブロックのヒープにコピーを作成します。また、以下に示すように、Block Implementation Specification - Clang 3.4 Documentationで説明されているように、内側のブロックのインポートされた変数もヒープにコピーされます。

OPの例では、「ブロック参照のインポートされたconstコピー」があります。

私は仕様の例を使用しています:

void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();

仕様には、関数copy_helperdispose_helper関数が必要であると記載されています。

copy_helper 関数には、既存のスタック ベースのポインターと新しいヒープ バージョンへのポインターの両方が渡され、ランタイムにコールバックして、ブロック内のインポートされたフィールドで実際にコピー操作を実行する必要があります。

仕様の次のコード例は解読が困難です (実際には、外側のブロックがヒープにコピーされたときに何が起こるかについての説明がありません)。とにかく、仕様では、内側のブロックのインポートされた変数が外側のブロックの raw ストレージ領域に (再帰的に) コピーされることを示そうとしているようです。

外側のブロックがヒープにコピーされると、内側のブロックのインポートされた変数も最終的にヒープに存在するようです。

直感的には、これはすべて理にかなっています。

これを実証するための小さなテスト プログラムを作成しました。

#import <Foundation/Foundation.h>


void foo(int param)
{
    int x0 = param;
    int x1 = param + 1;
    void (^existingBlock)(void) = ^{
        int y0 = x0;
        int y1 = x1;
        printf("&y0: %p\n", &y0);
        printf("&y1: %p\n", &y1);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    };

    void (^vv)(void) = ^{
        int y2 = x0;
        int y3 = x1;
        existingBlock();
        printf("&y2: %p\n", &y2);
        printf("&y3: %p\n", &y3);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    };

    printf("Stack: &x: %p\n", &x0);
    printf("Stack: &x: %p\n", &x1);

    printf("------- on main thread -------\n");
    vv();

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        printf("------- on thread 2 -------\n");
        assert(vv);
        sleep(1);
        int y4 = x0;
        int y5 = x1;
        vv();
        printf("&y4: %p\n", &y4);
        printf("&y5: %p\n", &y5);
        printf("&x0: %p\n", &x0);
        printf("&x1: %p\n", &x1);
    });
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {

        foo(1);
        sleep(2);
    }
    return 0;
}

出力は次のとおりです。

Stack: &x: 0x7fff5fbff868
Stack: &x: 0x7fff5fbff864
------- on main thread -------
&y0: 0x7fff5fbff70c
&y1: 0x7fff5fbff708
&x0: 0x1001081e0
&x1: 0x1001081e4
&y2: 0x7fff5fbff76c
&y3: 0x7fff5fbff768
&x0: 0x10010a588
&x1: 0x10010a58c
------- on thread 2 -------
&y0: 0x1000e5d9c
&y1: 0x1000e5d98
&x0: 0x1001081e0
&x1: 0x1001081e4
&y2: 0x1000e5dfc
&y3: 0x1000e5df8
&x0: 0x10010a588
&x1: 0x10010a58c
&y4: 0x1000e5e6c
&y5: 0x1000e5e68
&x0: 0x10010a5e8
&x1: 0x10010a5ec

ブロックがメイン スレッドで実行されると、ブロックはスタック上に存在します (ローカル変数とインポートされた変数のアドレスで示されます)。ランタイムを介しdispatch_asyncて実行されると、ブロックのローカル変数とインポートされた変数のアドレスからわかるように、内部ブロックを含むブロックがコピーされます。

関数にブレークポイントを設定できます。実際、ブロックをヒープcopy_helper_blockにコピーするために、プログラムはそこで一度停止します。vv

ここに画像の説明を入力

于 2013-08-10T08:41:17.930 に答える
5

Appleドキュメントからdispatch_async:

ブロック

The block to submit to the target dispatch queue. This function performs Block_copy and Block_release on behalf of callers. This parameter cannot be NULL.

だから、async_blockコピーされます。

この議論によると、blockasync_blockあなたの例では内側)、内側になりreadonly copyますasync_block

于 2013-08-10T01:16:13.817 に答える
0

block通常のオブジェクトと同様retainに、別のブロックに引っかかるために編集されます (個別にコピーされません) 。ブロックのコピーは、ブロックをコピーしたオーバーライドされたメソッドを呼び出しasync_blockたメッセージがブロック オブジェクトに送信された結果です。[block retain]retain

于 2013-08-10T01:15:49.757 に答える