これにより、何らかの保持サイクルが発生しますか? 安全に使用できますか?
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
これにより、何らかの保持サイクルが発生しますか? 安全に使用できますか?
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
コードには保持サイクルがmyBlock
含まれていますが、再帰の基本ケース ( ) で nil に設定することにより、再帰の終わりに保持サイクルを中断できますi == 0
。
これを証明する最善の方法は、「停止時に記録されていないデータを破棄する」をオフにし、「参照カウントを記録する」をオンにし、「アクティブな割り当てのみを追跡する」をオフにして、割り当てインストルメントの下で実行することです。
OS X Command-Line Tool テンプレートを使用して新しい 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;
}
次に、上で説明した設定を使用して、割り当てインストルメントの下で実行しました。次に、Instruments で「Statistics」を「Console」に変更して、プログラムの出力を確認しました。
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
) をコピーし、「Console」を「Objects List」に変更して、アドレスを検索ボックスに貼り付けました。楽器はブロックの割り当てだけを示しました:
Live 列の下のドットは、プログラムが終了したときにブロックがまだ割り当てられていたことを意味します。漏れました。アドレスの横にある矢印をクリックして、ブロックの割り当ての完全な履歴を表示しました。
この割り当てで発生したことは 1 つだけです。それは、割り当てられたことです。
次に、ステートメントmyBlock = nil
内の行のコメントを外しました。if (i == 0)
次に、プロファイラーの下で再度実行しました。システムはセキュリティのためにメモリ アドレスをランダム化するため、検索バーをクリアしてから、この実行でブロックのアドレスを再度コンソールで確認しました。今回でし0x7fc7a1424700
た。再び「オブジェクト リスト」ビューに切り替えて、新しいアドレスを貼り付けました0x7fc7a1424700
。これが私が見たものです:
今回は Live 列の下にドットがありません。これは、プログラムが終了するまでにブロックが解放されたことを意味します。次に、アドレスの横にある矢印をクリックして、完全な履歴を表示しました。
今回は、ブロックが割り当てられ、解放され、解放されました。
ARC を使用している場合は、__block
オブジェクト変数がブロックによって保持されるため、保持サイクルがあります。したがって、ブロックはそれ自体を保持します。myBlock
と の両方__block
を宣言することで回避できます__weak
。
MRC を使用している場合、__block
オブジェクト変数は保持されず、問題はないはずです。最後にリリースすることを忘れないmyBlock
でください。
警告が表示されないソリューションが必要でした。このスレッドで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
彼の説明は完全に理にかなっています。
いいえ、保持サイクルは発生しません。__block
キーワードはブロックにコピーしないように指示します。myBlock
これは、割り当ての前に発生し、アプリケーションがクラッシュする可能性があります。これが ARC でない場合はmyBlock
、 を呼び出した後にリリースするだけですmyBlock(10)
。
この問題に対する最新の解決策は次のとおりです。
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);