杰弗里·托马斯的答案是接近,但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