So here is what I've got:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), ^{
bool ready = some_function();
if( ready ) {
do_smth_here()
} else {
//invoke this block one more time after 0.1 sec
}
});
The problem is how can I get the reference to the current block?
Instead of jumping through the hoops shown above, I typically declare an instance method that I can call that, internally, takes care of the retriggers as necessary. That way, any given block is one-shot, but the re-trigger creates a new block.
As long as the block creation isn't terribly expensive -- which it won't be if the state is coming from whatever encapsulates the instance method -- it is efficient enough and a heck of a lot simpler.
- (void) retriggerMethod
{
... do stuff here, assuming you want to do it on first invocation ...
dispatch_after( ..., ^{
[self retriggerMethod];
});
}
You can restructure it as needed. And you can easily add a BOOL instance variable if you want to protect against simultaneous retriggers, etc...
This also provides a convenient hook for canceling; just add a BOOL to the instance that indicates whether the next invocation should really do anything and re-schedule.
Jeffrey Thomas's answer is close, but under ARC, it leaks the block, and without ARC, it crashes.
Without ARC, a __block
variable doesn't retain what it references. Blocks are created on the stack. So the callback
variable points to a block on the stack. When you pass callback
to dispatch_after
the first time (outside of the block), dispatch_after
successfully makes a copy of the block on the heap. But when that copy is invoked, and passes callback
to dispatch_after
again, callback
is a dangling pointer (to the now-destroyed block on the stack), and dispatch_after
will (usually) crash.
With ARC, a __block
variable of block type (like callback
) automatically copies the block to the heap. So you don't get the crash. But with ARC, a __block
variable retains the object (or block) it references. This results in a retain cycle: the block references itself. Xcode will show you a warning on the recursive dispatch_after
call: “Capturing 'callback' strongly in this block is likely to lead to a retain cycle”.
To fix these problems, you can copy the block explicitly (to move it from the stack to the heap under MRC) and set callback
to nil (under ARC) or release it (under MRC) to prevent leaking it:
__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 drop the #if
and just use the branch appropriate for your memory management.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With