ブロックと変数 翻訳元
この記事ではブロックと変数間の内部的な働きをメモリ管理も含めて説明します。
このページの最終更新:2010-05-12
ADCの最終更新:2009-10-19
ADCの最終更新:2009-10-19
目次
変数の型(Types of Variable)
ブロックオブジェクトのコードの集合では、変数は五つの異なる形で扱われます。
三つの標準的な変数の型は関数内と全く同様に参照することができます。
三つの標準的な変数の型は関数内と全く同様に参照することができます。
- グローバル変数。静的変数を含む。
- グローバル関数(厳密には変数ではない)。
- スコープに囲まれたローカル変数とパラメータ。
またブロックは他に二つの変数の型をサポートしています。
- [At function level are __block variables.]これらはブロック(とスコープ)内で変更可能で、参照されているどんなブロックがヒープにコピーされても保護されます。
- constの取り込み
最後に、メソッドの実装中ではブロックはObjective-Cインスタンス変数を参照できます。「オブジェクトとブロック変数(Object and Block Variables)」を参照してください。
ブロック中で使用される変数には以下の規則が適用されます。
ブロック中で使用される変数には以下の規則が適用されます。
- グローバル変数とレキシカルスコープ中に存在する静的変数はアクセス可能です。
- ブロックに渡されたパラメータ(ちょうど関数の引数のように)はアクセス可能です。
- レキシカルスコープに囲まれた局所的なスタック(静的でない)変数はconst変数として切り取られます。
この変数はプログラム中でブロック表現にさしかかった時に取得されます。ネストされたブロックでは、この値は最も近いスコープから切り取られます。 - レキシカルスコープ中で__blockストレージ型修飾子で宣言された変数は参照で提供されるので変更可能になります。
そのレキシカルスコープ中での変更は同じスコープで宣言された他のブロックのも含め全て反映されます。これらは「__block ストレージ型(The __block Storage Type)」で詳説しています。 - ブロック中のレキシカルスコープ中で宣言されたローカル変数は関数中のローカル変数と全く同じように働きます。
ブロックの呼び出しではそれぞれこの変数の新しいコピーが作られます。これらの変数はブロック中のブロックでconstや参照変数として代わる代わる使うことができます。
以下の例では静的でないローカル変数の用法を説明しています。
int x = 123; void (^printXAndY)(int) = ^(int y) { printf("%d %d\n", x, y); }; printXAndY(456); // prints: 123 456 |
注意してほしいのは、ブロック中でxで新しい値を参照しようとするとエラーになることです。
int x = 123; void (^printXAndY)(int) = ^(int y) { x = x + y; // error printf("%d %d\n", x, y); }; |
変数をブロック中で変更できるようにするには、__blockストレージ型修飾子を使用します。「__block ストレージ型(The __block Storage Type)」をご覧ください。
__block ストレージ型(The __block Storage Type)
__block型修飾子を適用することによって読み込んだ変数を変更可能であること、つまり読み書き可能であることを明示できます。__blockストレージ型はローカル変数を修飾するregister、auto、staticストレージ型と似ていますが、同時に使うことはできません。
__block変数はその変数のレキシカルスコープと、その中で生成または宣言された全てのブロックとその複製で共有されるストレージに確保されます。したがって、スタックフレームが破棄されても(例えば後で実行する為にどこかに待機させることで)、その中で宣言されたブロックの複製が生存していれば、ストレージは決して破棄されません。一つのレキシカルスコープ中の複数のブロックは共有する変数を同時に使うことがあります。
最適化の為に、ブロックストレージはブロック自体と同じようにまずスタック外に確保されます。もしブロックがBlock_copyを使って(またはObjective-Cでブロックにcopyを送って)コピーされれば、変数はヒープにコピーされます。つまり、__block変数のアドレスは時間とともに変わることがあります。
__block変数にはもう二つの制限があり、可変長の配列がなること、C99可変長配列を含む構造体がなることはできません。
以下の例では__block変数の用法を説明しています。
__block変数はその変数のレキシカルスコープと、その中で生成または宣言された全てのブロックとその複製で共有されるストレージに確保されます。したがって、スタックフレームが破棄されても(例えば後で実行する為にどこかに待機させることで)、その中で宣言されたブロックの複製が生存していれば、ストレージは決して破棄されません。一つのレキシカルスコープ中の複数のブロックは共有する変数を同時に使うことがあります。
最適化の為に、ブロックストレージはブロック自体と同じようにまずスタック外に確保されます。もしブロックがBlock_copyを使って(またはObjective-Cでブロックにcopyを送って)コピーされれば、変数はヒープにコピーされます。つまり、__block変数のアドレスは時間とともに変わることがあります。
__block変数にはもう二つの制限があり、可変長の配列がなること、C99可変長配列を含む構造体がなることはできません。
以下の例では__block変数の用法を説明しています。
__block int x = 123; // xはブロックストレージ中に確保される void (^printXAndY)(int) = ^(int y) { x = x + y; printf("%d %d\n", x, y); }; printXAndY(456); // prints: 579 456 // 今xは579 |
以下の例ではいくつかの異なる型の変数とブロック間の相互作用を示しています。
extern NSInteger CounterGlobal; static NSInteger CounterStatic; ;{ NSInteger localCounter = 42; __block char localCharacter; void (^aBlock)(void) = ^(void) { ++CounterGlobal; ++CounterStatic; CounterGlobal = localCounter; // localCounter fixed at block creation localCharacter = 'a'; // sets localCharacter in enclosing scope }; ++localCounter; // unseen by the block localCharacter = 'b'; aBlock(); // execute the block // localCharacter now 'a' } |
オブジェクトとブロック変数
ブロックはObjective-CとC++のオブジェクトや別ブロックへのサポートを変数という形で提供しています。
Objecive-Cオブジェクト
リファレンスカウンタ環境下では、ブロック中でObjective-Cオブジェクトを参照するときには通常そのオブジェクトを保持します。これはオブジェクトのインスタンス変数を単純に参照する場合でも当てはまります。ですが、__blockストレージ型修飾子のついたオブジェクト変数は保持されません。
注:ガベージコレクタ環境下では、__weak修飾子や__block修飾子を変数に適用した場合、そのブロックではその変数が確保されていることを保証できません。 |
ブロックをメソッドの実装内で使用した場合、オブジェクトインスタンス変数のメモリ管理の規則はさらに巧妙になります。
- インスタンス変数に参照でアクセスしている場合には、selfが保持されます。
- インスタンス変数に値でアクセスしている場合には、その変数が保持されます。
以下の例ではこの異なる二つの状況を説明しています。
dispatch_async(queue, ^{ // instanceVariableは参照なのでselfが保持されます doSomethingWithObject(instanceVariable); }); id localVariable = instanceVariable; dispatch_async(queue, ^{ // localVariableは値なので、localVariableが保持されます(selfではありません) doSomethingWithObject(localVariable); }); |