Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating the loop using GCD

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?

like image 886
givi Avatar asked Feb 21 '23 04:02

givi


2 Answers

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.

like image 156
bbum Avatar answered Mar 07 '23 19:03

bbum


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.

like image 37
rob mayoff Avatar answered Mar 07 '23 19:03

rob mayoff