Jeffrey Thomas の答えは近いですが、ARC の下ではブロックがリークし、ARC がないとクラッシュします。
ARC がないと、__block
変数はそれが参照するものを保持しません。ブロックはスタック上に作成されます。したがって、callback
変数はスタック上のブロックを指します。初回(ブロック外)に渡すcallback
と、正常にヒープ上にブロックのコピーが作成されます。しかし、そのコピーが呼び出されて再び渡されると、(スタック上の現在破棄されているブロックへの) ぶら下がっているポインターになり、(通常は) クラッシュします。dispatch_after
dispatch_after
callback
dispatch_after
callback
dispatch_after
ARC では、__block
ブロック型の変数 ( などcallback
)はブロックを自動的にヒープにコピーします。したがって、クラッシュすることはありません。しかし、ARC では、__block
変数はそれが参照するオブジェクト (またはブロック) を保持します。これにより、保持サイクルが発生します。つまり、ブロック自体が参照されます。dispatch_after
Xcode は再帰呼び出しに関する警告を表示します。
これらの問題を修正するには、ブロックを明示的にコピーして (スタックから MRC の下のヒープに移動するため)、callback
nil に設定する (ARC の下で) か、ブロックを解放して (MRC の下で) リークを防ぐことができます。
__block void (^callback)() = [^{
if(stop_) {
NSLog(@"all done");
#if __has_feature(objc_arc)
callback = nil; // break retain cycle
#else
[callback release];
#endif
} else {
NSLog(@"still going");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);
}
} copy];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);
明らかに#if
、メモリ管理に適したブランチを削除して使用することができます。