Creating a loop using GCD

So here is what I have:

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 a link to the current block?

+4
source share
3 answers

Instead of jumping through the hoops shown above, I usually declare an instance method, which I can call one that internally retrieves as needed. Thus, any given block is one-time, but restarting creates a new block.

Until the creation of the block is expensive - which will not happen if the state proceeds from the fact that it encapsulates the instance method - this is quite effective and damn much easier.

 - (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.

It also provides a convenient hook for lifting; just add BOOL to the instance, which indicates whether the next call should really do anything and reschedule.

+4
source

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.

+3
source

I think this is the code you are looking for:

 __block void (^callback)(); callback = ^{ bool ready = some_function(); if( ready ) { do_smth_here() } else { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); } }; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 

Thanks ^ Block Tips and Tricks

+1
source

All Articles