傑弗裏·托馬斯的答案是接近,但ARC下,它泄漏塊,沒有ARC,它崩潰。
沒有ARC,__block
變量不保留它引用的內容。塊在堆棧上創建。所以callback
變量指向堆棧上的一個塊。當您第一次(塊外)通過callback
到dispatch_after
時,dispatch_after
成功複製了堆上的塊。但是當這個副本被調用時,並且再次通過callback
到dispatch_after
,callback
是一個懸掛指針(到堆棧上現在被銷燬的塊),並且dispatch_after
將(通常)崩潰。
使用ARC,塊類型的__block
變量(如callback
)自動將塊複製到堆中。所以你不會崩潰。但使用ARC時,__block
變量將保留其引用的對象(或塊)。這會導致一個保留循環:塊引用它本身。 Xcode會在遞歸調用dispatch_after
時向您顯示警告:「在此塊中強烈捕獲'回調'可能會導致保留週期」。
要解決這些問題,你可以明確地callback
複製塊(它從堆棧移動到MRC下堆),並設置爲零(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
,只是使用適合你的內存管理的分支。
看起來你很喜歡使用GCD循環。我想知道使用它而不是設置NSTimer的優點是什麼? – Gon
@Gon個人喜好,真的,但有一些差異。例如,'dispatch _ *()'可以讓你嚴格控制隊列。由於我很懶,我也非常喜歡Xcode代碼完成時粘貼的片段。 :) – bbum