Objective-Cの__block
キーワードとはどういう意味ですか?ブロック内の変数を変更できることは知っていますが、知りたいのですが….
それは、それによってマークされたどんな変数もそれがブロックの中で使われるとき特別な方法で扱われなければならないことをコンパイラに伝えます。通常、ブロックでも使用されている変数とその内容はコピーされるため、これらの変数に対して行われた変更はブロックの外には表示されません。それらが__block
でマークされているとき、ブロックの内側で行われた変更はそれの外側でも見えます。
例と詳細については、AppleのBlocks Programming Topicsの The __blockストレージタイプ を参照してください。
重要な例はこれです:
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'
}
この例では、ブロックが呼び出される前にlocalCounter
とlocalCharacter
の両方が変更されます。ただし、__block
キーワードのおかげで、ブロック内ではlocalCharacter
に対する変更のみが表示されます。逆に、ブロックはlocalCharacter
を変更することができ、この変更はブロックの外側から見えます。
@bbumは ブログ記事 でブロックを詳細にカバーし、__ blockストレージタイプに触れます。
__ blockは異なる記憶域タイプです
静的、自動、揮発性のように、__ blockはストレージタイプです。それは、変数の記憶領域が異なる方法で管理されるべきであることをコンパイラに伝えます。
...
ただし、__ block変数の場合、ブロックは保持されません。必要に応じて、保持し解放するのはあなた次第です。
...
ユースケースに関しては__block
は引数を保持しないので保持サイクルを避けるために時々使用されます。一般的な例はselfを使うことです。
//Now using myself inside a block will not
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
__ blockは、2つの方法で使用できる記憶域修飾子です。
変数が、元の変数の字句スコープとそのスコープ内で宣言されたブロックとの間で共有されている記憶域に存在することをマークします。そしてclangはこの変数を表現するための構造体を生成し、この構造体を(値ではなく)参照として使います。
MRCでは、__ blockを使用して、ブロックがキャプチャするオブジェクト変数を保持しないようにすることができます。これがARCでは機能しないことに注意してください。 ARCでは、代わりに__ weakを使用してください。
詳しくは Apple doc を参照してください。
通常、__ blockを使用しない場合、ブロックは変数をコピー(保持)します。したがって、変数を変更しても、ブロックは古いオブジェクトにアクセスできます。
NSString* str = @"hello";
void (^theBlock)() = ^void() {
NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"
これら2つの場合には__block:が必要です
1.ブロックの内側の変数を変更して、外側に見えるようにしたい場合は、
__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"
2.ブロックを宣言した後で変数を変更したい場合、ブロックに変更が反映されるはずです。
__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"
__block
は、スコープ内で変数を変更可能にするために使用される記憶域タイプです。ブロックは読み取り専用コピーではありません。詳細は iOSのBlocks Programming を参照してください。
ブロック言語仕様 より:
新しいBlock型に加えて、ローカル変数に新しい記憶域修飾子__blockも導入しました。 [testme:ブロックリテラル内の__block宣言] __block記憶域修飾子は、既存のローカル記憶域修飾子auto、register、およびstaticと相互に排他的です。最後の使用後に自動的に回復します。実装は、ストレージが最初は自動で、参照元ブロックのBlockコピー時に割り当てられた(ヒープ)ストレージにのみ「移動」される最適化を選択することができます。このような変数は、通常の変数と同じように変異する可能性があります。
__block変数がBlockの場合、__ block変数は割り振られた記憶域に存在し、それ自体も割り振りされた記憶域にあるブロックを参照していると見なされます(Block_copy操作の結果です)。それにもかかわらず、実装がブロックのための初期自動ストレージを提供する場合、Block_copyまたはBlock_releaseを実行するための規定はありません。これは、潜在的に複数のスレッドが共有変数を更新しようとしているという固有の競合状態と、古い値の破棄および新しい値のコピーに関する同期の必要性によるものです。そのような同期はこの言語仕様の範囲を超えています。
__block変数が何にコンパイルされるべきかについての詳細は、 ブロック実装仕様 、セクション2.3を参照してください。
これがあなたを助けることを願っています
次のようなコードがあるとしましょう。
{
int stackVariable = 1;
blockName = ^()
{
stackVariable++;
}
}
ブロック内のスタック変数はデフォルトでは不変であるため、 "variable is not assignable"のようなエラーを出します。
宣言の前に__block(storage modifier)を追加すると、ブロック内で変更可能になります。つまり、__block int stackVariable=1;
これは接頭辞である変数がブロック内で使用できることを意味します。