「__block」キーワードはどういう意味ですか?


445

__blockObjective-Cのキーワードは正確にはどういう意味ですか?ブロック内の変数を変更できることは知っていますが、知りたいのですが...

  1. それは正確にコンパイラに何を伝えますか?
  2. 他に何かしますか?
  3. それだけの場合は、そもそもなぜそれが必要なのでしょうか。
  4. ドキュメントのどこかにありますか?(私はそれを見つけることができません)。

3
ここと「ブロックと変数」セクションを確認してください。


1
@Code Monkey:構文全般ではなく、キーワードについて具体的に質問しました。したがって、それが実際に重複しているとは考えないでください。
mjisrawi 2011

3
@Code Monkey:いいえ、これは重複ではありません。あなたが言及する質問はまったく話しません__block
DarkDust 2011

3
そして、誰かがObjective-C __blockがSwiftにどのように変換すべきか疑問に思っている場合:「[Swift内の]クロージャは、[Objective-C内の]ブロックと同様のキャプチャセマンティクスを持っていますが、1つの重要な点で異なります。変数はコピーではなく変更可能です。つまり、Objective-Cでの__blockの動作は、Swiftでの変数のデフォルトの動作です。」Appleの本から:CocoaおよびObjective-C(Swift 2.2)でのSwiftの使用。
JariKeinänen16年

回答:


542

マークされた変数は、ブロック内で使用する場合は特別な方法で処理する必要があることをコンパイラーに伝えます。通常、ブロックで使用されている変数とその内容はコピーされるため、これらの変数に加えられた変更はブロックの外側には表示されません。でマークされている場合__block、ブロックの内側で行われた変更は、ブロックの外側にも表示されます。

例と詳細については、「アップルのブロックプログラミングトピック」の「__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が変更されています。ただし、ブロックの内部ではlocalCharacter__blockキーワードのおかげで、への変更のみが表示されます。逆に、ブロックは変更可能でlocalCharacterあり、この変更はブロックの外側に表示されます。


8
優れた簡潔な説明と非常に役立つ例。ありがとう!
エヴァン・ストーン

1
aBlockはどのようにlocalCounterを変更しますか?CounterGlobalを変更するだけのようです。ありがとう
CommaToast 2013年

8
変更はしませんlocalCounterが、変更しlocalCharacterます。また、localCounterブロック内の値に注意してください。ブロックが呼び出される、ただしブロックが作成された(つまり、値が「取得」されたとき)に変数が増加しても、値は42 です。
DarkDust 2013年

1
それは有用な説明ですが、説明の中で矛盾していると思われるものを説明できますか?上記で「aBlockは... localCounterを変更する」と言い、コメントで「[aBlock]はlocalCounterを変更しない」と言います。どっち?「変更されていない」場合、回答を編集する必要がありますか?
Praxiteles 2014年

2
一般に、__ blockのない変数は値によって取得され、ブロックの作成時にブロックの「環境」にパックされます。ただし、__ block変数はキャプチャされません。ブロックの内部または外部で使用された場合は、そのまま参照されます。
jchnxu 14

27

@bbumはブログ投稿の詳細なブロックをカバーし、__ blockストレージタイプに触れます。

__blockは別個のストレージタイプです

static、auto、volatileと同様に、__ blockはストレージタイプです。これは、変数のストレージが異なる方法で管理されることをコンパイラーに通知します。

...

ただし、__ block変数の場合、ブロックは保持されません。必要に応じて、保持および解放するのはユーザーの責任です。
...

ユースケース__blockに関しては、引数を保持しないため、保持サイクルを回避するために使用される場合があります。一般的な例は、自己の使用です。

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;

保持サイクルの問題の詳細については、この投稿を参照してください:benscheirman.com/2012/01/…。う__weakとしても、この特定のケースで十分?多分それはもっとはっきりしています...
Hari Karam Singh

17
最後に、__ blockを使用して強い参照サイクル(別名保持サイクル)を回避できるという主張は、ARCコンテキストでは明らかに間違っています。ARC __blockでは変数が強く参照されるため、実際にはその可能性が高くなります。stackoverflow.com/a/19228179/189006
クリシュナン

10

通常、__ 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"

8

__blockは、次の2つの方法で使用できるストレージ修飾子です。

  1. 変数が、元の変数の字句スコープとそのスコープ内で宣言されたブロックの間で共有されるストレージに存在することを示します。そして、clangはこの変数を表す構造体を生成し、この構造体を(値ではなく)参照によって使用します。

  2. MRCでは、__ blockを使用して、ブロックがキャプチャするオブジェクト変数の保持を回避できます。これがARCでは機能しないことに注意してください。ARCでは、代わりに__weakを使用する必要があります。

詳細については、アップルのドキュメントを参照してください。



2

これがあなたを助けることを願っています

次のようなコードがあるとします。

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

ブロック内のスタック変数はデフォルトで不変であるため、「変数は割り当てできません」のようなエラーが発生します。

宣言の前に__block(storage modifier)を追加すると、ブロック内で変更可能になります。 __block int stackVariable=1;


1

ブロック言語仕様

新しいBlock型に加えて、ローカル変数用の新しいストレージ修飾子__blockも導入しています。[testme:ブロックリテラル内の__block宣言] __blockストレージ修飾子は、既存のローカルストレージ修飾子auto、register、staticと相互に排他的です。[testme] __blockで修飾された変数は、割り当てられたストレージにあるかのように機能し、このストレージは上記の変数を最後に使用した後に自動的に回復しました。実装は、ストレージが最初は自動であり、参照ブロックのBlock_copy時に割り当てられた(ヒープ)ストレージにのみ「移動」される最適化を選択できます。このような変数は、通常の変数と同じように変更できます。

__block変数がBlockである場合、__ block変数は割り当てられたストレージにあると想定する必要があり、そのため、割り当てられたストレージにもあるBlockを参照すると想定されます(Block_copy操作の結果です)。これにもかかわらず、実装がブロックの初期自動ストレージを提供する場合、Block_copyまたはBlock_releaseを実行するための規定はありません。これは、共有変数を更新しようとする潜在的にいくつかのスレッドの固有の競合状態と、古い値の破棄と新しい値のコピーに関する同期の必要性が原因です。このような同期は、この言語仕様の範囲を超えています。

__block変数のコンパイル先の詳細については、ブロック実装仕様のセクション2.3を参照してください。


あなたのリンクは両方とも死んでいます
Ben Leggiero 2016年

これは実際の答えではなく、具体化または削除される可能性があります。ありがとう!
Dan Rosenstark

0

これは、それがプレフィックスである変数がブロック内で使用できることを意味します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.