5

同時実行プログラミング ガイドによると、次のようになります。

ブロックがディスパッチ キューに追加されるとき、これらの値は通常、読み取り専用の形式のままにしておく必要があります。ただし、同期的に実行されるブロックは、先頭に __block キーワードが付加された変数を使用して、親の呼び出しスコープにデータを返すこともできます。

キューの外で作成された変数を変更してきましたが、それらを指定したことがない__blockので、正確にいつ、またはなぜこれが必要なのか疑問に思っています。それとも、インスタンス変数は常にブロックによって本質的に変更可能であり、あたかも__block舞台裏から割り当てられているのでしょうか?

更新:非同期キューを使用していることも追加する必要がありますが、上記では、変数は同期キューでのみ変更できると述べています( with __block

4

2 に答える 2

2

iVarブロック内のクラスのインスタンス変数へのアクセスは、コンパイラによって として解釈されself->iVarます。selfしたがって、ブロックは変更されていないをキャプチャします。

__block修飾子は にも機能すると確信しているdispatch_asyncため、これはドキュメントのエラーである可能性があります。

追加した

次の例は、__block変数を で使用する方法を示していdispatch_asyncます。

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
__block int total = 0;
printf("address of total: %p\n", &total);

// dispatch some (concurrent) blocks asynchronously:
dispatch_async(queue, ^{
    OSAtomicAdd32(5, &total);
});
dispatch_async(queue, ^{
    OSAtomicAdd32(7, &total);
});

// Wait for all blocks to complete:
dispatch_barrier_sync(queue, ^{ });

printf("address of total: %p\n", &total);
printf("total=%d\n", total);

出力:

address of total: 0x7fff5fbff8f0
address of total: 0x100108198
total=12

totalブロックが実行されると、スタックからヒープにコピーされることがわかります。

追加した

Blocks Programming Guideでこれを見つけました。__block非同期ブロックで変数を使用しても問題がない理由を説明します。

__block 変数は、変数のレキシカル スコープと、変数のレキシカル スコープ内で宣言または作成されたすべてのブロックおよびブロック コピーとの間で共有されるストレージに存在します。したがって、フレーム内で宣言されたブロックのコピーがフレームの終わりを超えて存続する場合 (たとえば、後で実行するためにどこかでキューに入れられることによって)、ストレージはスタック フレームの破棄に耐えます。特定のレキシカル スコープ内の複数のブロックは、共有変数を同時に使用できます。

最適化として、ブロック ストレージはブロック自体と同じようにスタックから開始します。ブロックが Block_copy を使用して (またはブロックがコピーを送信された場合は Objective-C で) コピーされる場合、変数はヒープにコピーされます。したがって、__block 変数のアドレスは時間の経過とともに変化する可能性があります。

于 2012-08-11T14:47:56.623 に答える
2

Concurrency Programming Guide からの引用は、関数またはメソッド内で作成される変数に関するものです。それらの内部で作成された変数は、関数呼び出しの間それらを保持するスタック上に作成されます。つまり、関数が戻ると、そのスタック フレームは変数と共に破棄されます。

Objective-c のオブジェクトはヒープ上に作成されるため、関数が戻った後も存続できますが、次のような行を作成すると:

MyClass *object = [[MyClass alloc] init];

objectヒープに配置されるオブジェクトを作成していますが、ヒープ内のオブジェクトへのポインターを保持する変数も作成しています。その変数は、現在のメソッド/関数のスタック フレームに配置され、返された後に破棄されます。オブジェクトを解放しないと、関数が終了してもオブジェクトは破棄されません。

そのため、ブロックは、ブロック内で参照される親のスコープ変数をコピーします。それらは関数呼び出しの最後に破棄される可能性があるため、ブロックのプライベート メモリにコピーされます。ご存知のように、その後ブロックを使用できます。そのため、スタックから変数をコピーして直接使用しないようにブロックに通知するために指定子を使用する必要があります。__blockこの変数は関数が戻るときに破棄される可能性があるため、関数が戻る前にブロックが実行されることが保証されるため、その場合は同期ディスパッチのみを使用する必要があると Concurrency Programming Guide が指摘しています。 編集:Martin Rが正しく指摘したように、__block変数は常にスタックから直接使用されるとは限りません。この資料としてつまり、それらはポインターのように扱われ、ブロックがコピーされたときにアドレスをスタックから変更できます (ディスパッチのように)。コピーされた変数は、ブロックと関数の間で共有されるストレージに配置され、変数の寿命を延ばします。ローカル変数をdispatch_syncで使用する必要があると述べられている理由は、ARC以外の環境では変数が保持されない*ため、割り当て解除されたオブジェクトを使用する危険性があるためだと思います。同じ理由で、静的変数とインスタンス変数の前に __block を使用しないと思います。

インスタンス変数または静的変数である変数は、どの関数のスタックにも存在しないため、ブロックごとにコピーされず、指定子は必要ありません__block。静的変数はプログラムが実行されている限り存続しますが、インスタンス変数は、その変数を保持するオブジェクトが破棄されると破棄されます。そのため、ブロックには別の機能があります。ブロック内で参照されるすべてのオブジェクトは、ブロックの存続期間中保持され、オブジェクトが予期せずに解放されないようにします。

于 2012-08-11T15:51:17.400 に答える