20

これは保持サイクルの任意の種類につながりますか?使用するのは安全ですか?再帰ブロック保持サイクル

__block void (^myBlock)(int) = [^void (int i) 
{ 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    myBlock(i - 1); 
} copy]; 
myBlock(10); 

myBlock = nil; 
+1

私は再帰的なブロックでサイクルを保持解決するための簡単な方法を掲載しました。 ARCの下でも動作します:http://stackoverflow.com/a/14730061/439096 – Berik

答えて

34

あなたのコードは保持サイクルを含まない、しかし、あなたは再帰ベースケース(i == 0)にnilにmyBlockを設定することにより、再帰の最後に保持して悪循環を断ち切ることができます。

これを証明する最善の方法は、「停止時の未記録データを破棄する」をオフにして「レコード参照カウント」をオンにし、「アクティブな割り当てのみを追跡する」をオフにして、割り当て計器で実行することです。

OS Xコマンドラインツールテンプレートを使用して新しいXcodeプロジェクトを作成しました。ここにプログラム全体があります:

#import <Foundation/Foundation.h> 

void test() { 
    __block void (^myBlock)(int) = [^void (int i){ 
     if (i == 0) { 
//   myBlock = nil; 
      return; 
     } 
     NSLog(@"myBlock=%p %d", myBlock, i); 
     myBlock(i - 1); 
    } copy]; 
    myBlock(10); 
} 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     test(); 
    } 
    sleep(1); 
    return 0; 
} 

私は上で説明した設定で、割り当て計器でそれを実行しました。それから私は、プログラムの出力を確認するために、楽器の「統計」に「コンソール」に変更:

2012-10-26 12:04:31.391 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 10 
2012-10-26 12:04:31.395 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 9 
2012-10-26 12:04:31.396 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 8 
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 7 
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 6 
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 5 
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 4 
2012-10-26 12:04:31.399 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 3 
2012-10-26 12:04:31.400 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 2 
2012-10-26 12:04:31.401 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 1 
<End of Run> 

私はブロックアドレス(0x7ff142c24700)をコピーし、「リストオブジェクト」に「コンソール」に変更、およびにアドレスを貼り付け検索ボックス。

block leaked

ライブ列の下のドットは、プログラムが終了したときにブロックがまだ割り当てられていた意味:楽器は私のブロックのためだけの割り当てを示しました。それは漏れた。今までにこの割り当てで起こっ

block leaked detail

つだけの事:私は、ブロックの割り当ての完全な履歴を参照するために次のアドレスにある矢印をクリックし、それが割り当てられていました。

次は、if (i == 0)ステートメントのmyBlock = nil行のコメントを外しました。それから私は再びプロファイラの下でそれを走らせた。システムはセキュリティのためにメモリアドレスをランダム化するので、検索バーをクリアして、この実行時にブロックのアドレスを再度コンソールにチェックしました。今回は0x7fc7a1424700でした。私は再び "オブジェクトリスト"ビューに切り替え、新しいアドレス0x7fc7a1424700に貼り付けました。ここで私が見たものです:

block freed

ライブ列の下のドットブロックは、プログラムが終了した時点で解放されたことを意味し、この時間はありません。その後、私は完全な履歴を参照するために次のアドレスにある矢印をクリック:

block freed detail

この時間は、ブロックは、割り当てられたリリース、および解放されました。

+1

この場合、* __ブロック*記憶指定子を持っていてもブロックが強く捕捉されるのはなぜですか? –

+0

@RamyAlZuhouri clang ARCのドキュメントをお読みください。 [7.5節](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#blocks)は、「推論規則は、__block変数に等しく適用されます。これは、__block変数が非ARCでないセマンティクスからのシフトですキャプチャ中に暗黙に保持する ""推論 "は、[§4.4](http://clang.llvm.org/docs/AutomaticReferenceCounting)へのリンクです。html#ownership-inference)は、 "オブジェクトが保持可能なオブジェクト所有者タイプで宣言されていて、明示的な所有者修飾子がない場合、その型は暗黙のうちに__厳しい資格を持つように調整されています。" –

+0

@RamyAlZuhouriしたがって、ARCの下では、 '明示的な所有者修飾子を持たない変数は' __strong'修飾子であり、それが参照するオブジェクトを保持します。 –

1

いいえ、保持サイクルは発生しません。 __blockキーワードは、アプリケーションがクラッシュする割り当ての前に発生していた、myBlockをコピーしないようにブロックに指示します。これがARCでない場合は、myBlock(10)に電話した後、唯一行う必要があるのはmyBlockです。

+0

詳細はこちらをご覧ください:http://www.friday.com/bbum/2009/08/29/blocks-tips-tricks/ – Joe

+0

最初の通話後、または前回の通話後に放す必要がありますか? –

+0

完了したら'myBlock(10)'の呼び出しの後にブロックから解放しないでください。私は答えにそれを加えました。 – Joe

2

ARCを使用している場合は、オブジェクト変数がブロックによって保持されるため、保持サイクルがあります。__blockしたがって、ブロックはそれ自体を保持します。myBlock__block__weakの両方として宣言することで回避できます。

MRCを使用している場合は、__blockオブジェクト変数は保持されず、問題はありません。最後にmyBlockをリリースすることを忘れないでください。

13

ありますサイクルと潜在的な必要性を回避し、簡単な解決策は、途中でコピーするには:あなたがラッパーを追加することができます

void (^myBlock)(id,int) = ^(id thisblock, int i) { 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    void(^block)(id,int) = thisblock; 
    block(thisblock, i - 1); 
    }; 

myBlock(myBlock, 10); 

は、元の型シグネチャを取得するには:

void (^myBlockWrapper)(int) = ^(int i){ return myBlock(myBlock,i); } 

myBlockWrapper(10); 

これは大変な作業となります相互再帰を行うためにそれを拡張したいのであれば、私は最初にこれを行う良い理由を考えることができません(クラスは明確になりませんか?)。

+0

すばらしいソリューションです。何度もアニメーションを繰り返すのに最適です – Lucien

1

私は警告を取得していないソリューションを求めていましたし、このスレッドでhttps://stackoverflow.com/a/17235341/259521 Tammo Freeseのが最適なソリューション提供します:

__block void (__weak ^blockSelf)(void); 
void (^block)(void) = [^{ 
     // Use blockSelf here 
} copy]; 
blockSelf = block; 
    // Use block here 

彼の説明は完璧な理にかなっています。ここで

0

が問題に近代的なソリューションです。

void (^myBlock)(); 
__block __weak typeof(myBlock) weakMyBlock; 
weakMyBlock = myBlock = ^void(int i) { 
    void (^strongMyBlock)() = weakMyBlock; // prevents the block being delloced after this line. If we were only using it on the first line then we could just use the weakMyBlock. 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    strongMyBlock(i - 1); 
}; 
myBlock(10); 
関連する問題