Jeffrey Thomas's answer is close, but under ARC he loses the block, and without ARC it will work.
Without ARC, the __block
variable __block
not save what it refers to. Blocks are created on the stack. Thus, the callback
variable points to a block on the stack. When you pass a callback
to dispatch_after
for the first time (outside the block), dispatch_after
successfully makes a copy of the block on the heap. But when this copy is called and goes from callback
to dispatch_after
again, callback
is a dangling pointer (to the now destroyed block on the stack), and dispatch_after
will (usually) fail.
With ARC, a variable of type __block
type block (e.g. callback
) automatically copies the block to the heap. This way you will not get an accident. But with ARC, the __block
variable saves the object (or block) that it refers to. This leads to a save loop: the block refers to itself. Xcode will show you a warning about the recursive call dispatch_after
: βCapturing the callbackβ strongly in this block will probably lead to a save loop β.
To fix these problems, you can explicitly copy the block (to move it from the stack to the heap in the MRC) and set callback
to nil (in ARC) or release it (in MRC) to prevent it from leaking:
__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);
Obviously, you can remove #if
and just use a branch suitable for managing your memory.
source share