1

ブロックが Objective-C でどのように機能するかを理解しようとしています。Apple のドキュメントを読んでいるときに次の質問がありました (リンク)

ブロックを使用してはいけない例を次に示します。

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
    }
}

しかし、"hello, 0"、"hello, 1"、"hello, 2" を出力する 3 つの異なるブロックを取得するにはどうすればよいでしょうか? いろいろ試しましたが、毎回「こんにちは、2」が 3 回出ました。

4

2 に答える 2

8

ブロックはスタック上でライフを開始するため、ブロックのライフスパンはそれが宣言されているスコープの間だけです。

for() ループの本体 ({} 内のループの本体) は、それ自体がスコープです。したがって、あなたのコードは、スタック [ブロック] 上の何かへの参照を、周囲のスコープ [言語配列] の変数に入れています。

ブロックを存続させるには、ブロックをヒープにコピーする必要があります。

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = [^{ printf("hello, %d\n", i); } copy];
    }
}

ARC を使用しない場合は-release、ある時点でコピーされたブロックも必要になります。

このブログ投稿が便利だと思うかもしれません(Blocks が公開された直後に書きました)。これは、いくつかのヒント、トリック、および落とし穴に入ります。


待ってください -- そうです -- あなたの言う通りです。ブロックが魔法のようにヒープ上にあるように見える魔法が ARC コンパイラーで進行中です。ただし、この動作を明示的に文書化した LLVM ドキュメントには何も見つかりません。ARC をオフにすると、出力は 0,1,2 ではなく 2,2,2 のようになります。

これはやや新しい動作です。これがどのようにサポートされているかを正確に定義する明示的なメモがコンパイラで見つかるまで、私はこの動作に依存しません。


@autoreleasepool {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        void (^block)(void) = ^{ printf("hello, %d\n", i); };
        NSLog(@"%p", block);
        blockArray[i] = block;
        NSLog(@"%p", blockArray[i]);
    }

    for (int i = 0; i < 3; ++i) blockArray[i]();
}

出力:

2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2

したがって、ブロックはスタック上に作成され、 for() ループの範囲外の割り当てで自動的にヒープにコピーされます。

同様のテストでは、ブロックが引数として NSArray の addObject: に渡されたときにコピーされることも明らかになりました。

于 2012-12-24T22:37:15.830 に答える
4

本当にこれを機能させたい場合NSMutableArrayは、C 配列の代わりに を使用できます。 NSMutableArray *blocks = [NSMutableArray 配列];

for (int i = 0; i <= 3; i++) {
  blocks[i] = ^{ printf("hello %d\n", i); };
}

それらを に追加するとNSMutableArray、スタックからヒープにコピーされ、forループの範囲を超えて存続できるようになります。

@bbum が上記が機能しないことを指摘しているため、just workARC でブロックするという考えが行き過ぎていました。

ブロックを機能させるには、ブロックを積極的にコピーする必要があります...したがって、次のように機能するはずです

NSMutableArray *blocks = [NSMutableArray array];

for (int i = 0; i <= 3; i++) {
  blocks[i] = [^{ printf("hello %d\n", i); } copy];
}
于 2012-12-24T22:28:23.053 に答える