4

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?

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

It looks like you use GCD loop like this a lot. I wonder what's the advantages of using it rather than setting a NSTimer?
@Gon Personal preference, really, but there are some differences. dispatch_*() lets you tightly control the queue, for example. As I am lazy, I also really like the snippets pasted in by Xcode's code completion. :)
3

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.

Comments

1

I think this is the code your 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 to ^ Blocks Tips & Tricks

3 Comments

@robmayoff Leak? I don't see it. There is no retain cycle here. callback gets automatically retained by dispatch_after() and automatically released when the block completes. All the retains and releases cancel out. I don't doubt that there could be some defect in the implementation of blocks that would hold onto an extra reference, but I would have to see it in an analyzer.
See my answer. BTW, I tested that your solution crashes (under MRC) and leaks (under ARC) using malloc history logging.
@robmayoff What can I say? The compiler and runtime are the ultimate judge. Good solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.