17

self is merely a captured variable inside a block and doesn't reference the block itself, so how does a block reference itself without having an explicit captured variable for that purpose?

5 Answers 5

16
__block void(^strawberryFields)();
strawberryFields = [^{ strawberryFields(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               strawberryFields);
  • you use the __block because the block will make a copy of the value of strawberryFields when the block is created which will be before the assignment.

  • you also must copy the block prior to any other copy operation or else you'll end up with a block that references the on-stack original version.

  • note that the above code leaks the block. Somewhere, there needs to be a release of that block to balance the copy.

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

3 Comments

"Somewhere, there needs to be a release" why not right after the dispatch_async?
I think "Somewhere, there needs to be a release" means that strawberryFields needs to be set to nil at some point. Otherwise, it's "Strawberry fields forever..."
@AndrewRondeau This was written before ARC was really a thing. :)
5

I found this pattern to work and stable for ARC (automatic reference counting), both in Debug and Release builds.

-(void) someMethod
{
    // declare a __block variable to use inside the block itself for its recursive phase.
    void __block (^myBlock_recurse)();

    // define the block
    void (^myBlock)() = ^{
        // ... do stuff ...
        myBlock_recurse(); // looks like calling another block, but not really.
    };

    // kickstart the block
    myBlock_recurse = myBlock; // initialize the alias
    myBlock(); // starts the block
}

Initially I tried just putting a __block modifier to myBlock and use that variable directly to recurse within the block's implementation. That works on the ARC Debug build but breaks with an EXC_BAD_ACCESS on the Release build. On the other hand removing the __block modifier raises a "variable not defined when captured by block" warning (and I was reluctant to run it and test).

4 Comments

PS: crashes are in iOS 5.0, not sure about Mac.
can you show an example of code that crashes when you just use myBlock but does not crash when you use this myBlock_recurse thing?
In ARC, this causes a retain cycle
@newacct Temporary retain cycle – just make sure you assign nil to myBlock_recurse after recursion is done.
0

I have never tried this before and not 100% sure it's useful, if valid, but for example:

typedef void (^BasicBlock)(void);

__block BasicBlock testBlock;
testBlock  = ^{NSLog(@"Testing %p", &testBlock);};
testBlock();

You probably have declare the variable with __block to prevent self-retain cycle.

5 Comments

Code is decidedly odd, but more or less correct. The reasoning, though, is incorrect.
Sure; see my answer. The code wasn't that odd, really, just that printing the address of an __block variable is typically surprising (in that there is an implied indirection).
Yes, the address means nothing indeed. And I wasn't aware that the block will make a copy of the value before the assignment, so __block is necessary regardless of self retaining. Thank you for the comment. If you let me ask just one more thing, how does block actually deal with __block variable when writing to it? Does it make a reference to the original (and possibly in-stack) variable?
A __block variable is effectively a handle; a pointer to where the data is actually held. It starts on the stack. When you do a Block_copy() (or -copy), it allocates a bit of memory on the heap to hold the variables value, copies it, then updates the pointer in the __block to no longer point to the stack.
Thanks a lot. Then if I copy a block, in which I change the value of a __block variable, then the change is not visible inside the original scope?
0

The block needs some way to nil out its own reference. Typically it is done by storing the block in a property of the class.

Sometimes you can prefer to not use a property. Here is how you do it without a property:

    __weak id weakSelf = self;
    __block id block = ^{

        if(weakSelf) {

            // .. do whatever

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
        }
        else {

            block = nil;
        }
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);

The key thing to keep in mind is that all code paths must lead to a block = nil. We do that here by calling the block every 5 seconds until weakSelf turns nil.

3 Comments

As of XCode 5.0 (currently in beta) this throws a warning. The warning is wrong -- hopefully Apple makes this warning smarter in the future.
It's not wrong. You are causing a retain cycle (even if you try to break it later, which I'm not sure is correct). The better solution is to not have a retain cycle in the first place, by having the block capture a weak reference to itself.
If you're skeptical I suggest testing the code in XCode. Setting block = nil removes the retaining reference in arc. You can test it be setting a breakpoint at block = nil and copying the pointer to block in the pasteboard. Let the program run for a while (seems to take 1-5 seconds), pause the program, and po the pointer. In this way you can easily tell the object is destroyed.
0

Note that in ARC, it's a little different -- __block object pointer variables are by default retained in ARC, unlike in MRC. Thus, it will cause a retain cycle. It is necessary for the block to capture a weak reference to itself (using __weak) in order to not have a retain cycle.

However, we still need a strong reference to the block somewhere. If there are no strong references, the block (which is on the heap since it's copied) will be deallocated. Thus, we need two variables, one strong and one weak, and inside the block use the weak one to reference itself:

__block __weak void(^weakBlock)();
void(^myBlock)();
weakBlock = myBlock = [^{ weakBlock(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               myBlock);

4 Comments

What will happen if we don't give copy?
@trss: Some versions of the compiler might automatically insert a copy (so you might not see any difference), but this is not guaranteed. If a copy is not made, the problem is that weakBlock will point to the stack block. And even though we might later be using a copy of the block (heap block), inside the block, the code still refers to weakBlock, which points to the stack block, so it will execute the stack block and cause problems. That's why the block must be copied before assigning to weakBlock.
This doesn't work. It gives a warning about assigning to a weak variable (weakBlock), and the warning is right: weakBlock is deallocated immediately.
@puzzl: The warning is not right. It is assigned to myBlock which is a strong reference. An assignment expression evaluates to the result of the right side, and that result is assigned to a strong reference. The compiler is incorrectly picky about the ordering of the assignment. The code is correct according to the standard, but I have changed it to get rid of the warning.

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.