^ Blocks Tips & Tricks

Ok, I lied… next post about Blocks won’t be about the innards. At least not directly.

Having worked with Blocks for a while, I thought I’d share some tips, tricks and gotchas.

1) When declaring Blocks based API, always declare functions and methods to take only one block and always make that block the last argument.

A block is, in and of itself, a chunk of code. It is like an inline function with bits of data attached to it.

When passing a Block to various block-taking APIs on the system, it is far more convenient to simply declare the block inline with the function/method call than to first declare the block, assign it to a variable of appropriate type, and then pass it to the function/method.

Since Blocks are typically multiple lines of code, having the block be other than the last argument would break up the call site.

Likewise, taking more than one block as arguments would make the call site potentially unwieldy in length. It also introduces complexity; you now have two Blocks — two “units of work” — that are somehow intertwined. Better to let the Block be as simple as possible and, if needed, take care of rendezvousing their result by whatever mechanism is most appropriate.

Consider the dead simple simplicity of “>Grand Central Dispatch (which is available as an open source project). Through that simplicity an awesomely powerful tool for concurrency has been created.

2) Blocks are created on the stack. Careful.

Consider:

    typedef int(^Blocky)(void);
    Blocky b[3];
    
    for (int i=0; i<3; i++)
        b[i] = ^{ return i;};
    for (int i=0; i<3; i++)
        printf("b %d\n", b[i]());

You might reasonably expect the above to output:

0
1
2

But, instead, you get:

2
2
2

Since the block is allocated on the stack, the code is nonsense. It only outputs what it does because the Block created within the lexical scope of the for() loop’s body hasn’t happened to have been reused for something else by the compiler.

To fix, tell the block to copy itself. This will move it to the heap and each slot in b[] will, thus, be a distinct block with a distinct const copy captured i.

    typedef int(^Blocky)(void);
    Blocky b[3];
    
    for (int i=0; i<3; i++)
        b[i] = [^{ return i;} copy];
    for (int i=0; i<3; i++)
        printf("b %d\n", b[i]());

The above doesn’t leak for me because I’m using GC.

Note also that the -copy or Block_copy() will also update the innards of all __block variables to refer to the new location on the heap.

3)__block is a distinct storage type

Just like static, auto, and volatile, __block is a storage type. It tells the compiler that the variable’s storage is to be managed differently.

For __block, it tells the compiler that the variable may need to move to the heap and, thus, an extra bit of indirection is required to get to the variable’s value. This is all done transparently by the compiler. The overhead is minimal.

It does mean, though, that you can’t take the address of a __block variable without introducing a potentially very subtle and nasty bug in your code; the address may go invalid if anything copies the Block.

4) Blocks retain object references (but not all)

When a block is created, all of the variables used within the block that are from the surrounding lexical scope will be copied into the block.

Think of it this way; as execution passes over the point where a block is declared, the block takes a snapshot of all of the state that it uses within itself. In the above for() example, each block captured the current value of i as execution passed through the loop.

For captured values of type id — for Objective-C object pointers — the object is retained when the block is created (when execution passes over the block) and then released when the block is destroyed. This ensures that any “captured” objects survive as long as the block does.

However, for __block variables, the block does not retain. It is up to you to retain and release, as needed.

The reason for this behavior is because it wouldn’t make sense otherwise. Trying to automatically retain/release __block variables would have led to edge cases and concurrency issues that are the stuff of nightmares.

5) When should you copy or retain a block?

In general, you shouldn’t need to copy or retain a block.

Beyond 2 above, copying of a block is necessary when it is expected/normal for the block to be used after the scope within which it was declared is destroyed.

Copying a block, either via Block_copy() or -copy, effectively moves the block to a malloc’d chunk of memory on the heap.

Most of the time, anyway– when the compiler detects that a block is effectively constant because it captures no state, the compiler will create a static global for said block and copying is a no-op. Implementation detail. Now you know, but forget it when writing code.

The system provided APIs that take Blocks as arguments will copy said Blocks when warranted. For example, dispatch_async() will copy the passed in Blocks.

For a Block that hasn’t been copied, -retain doesn’t make sense. It does nothing. It could have been implemented to return a copy of the Block, but that would have been a bifurcation of the -retain contract beyond acceptable.

So, copy a Block if you want it to stick around beyond the end-of-life of the declarative scope. Once copied, treat it like an NSObject (because it is a subclass) for retain/release purposes.

6) Blocks are Objective-C Objects

Blocks have an isa slot and, when the runtime is in a process that is using the Objective-C runtime, the isa slot is filled with one of a handful of Block classes.

They cannot be subclassed. Nor is there anything terribly interesting about said classes.

Blocks do, however, respond to a handful of methods; -retain, -release, -autorelease, and -copy.

There is also a C based API for copying and releasing Blocks; Block_copy() and Block_release() in /usr/include/Block.h. Just like CFRetain vs. -release, if you use the C API to copy a block, don’t release it with -release (and vice-versa).

But, wait, 2) says Blocks start out on the stack…?

A Block is the one instance of an Objective-C object that starts on the stack. While you can happily collect together a bunch of Blocks in one of the Foundation provided collection classes (NSArray, NSDictionary, or NSSet), make sure you copy the Block before you do!

The last thing you want is a stack based object to end up in an autorelease pool….

7) Blocks can be recursive

I posted a recursive block on Twitter during the #songsincode meme (broken apart into one more line for clarity):

    __block void(^strawberryFields)();
    strawberryFields = ^{ strawberryFields(); };
    strawberryFields();

The trick is that you must use a __block variable as the reference to the recursive Block.

Without using the __block storage type, the strawberryFields reference within the block would be bound — would be const copied — before the assignment occurs, leading to a crash on the first recursive call.

As @bjh said below, if the block is going to be copied ever prior to invocation, you should make sure you copy the block and update strawberryFields variable with the copied block:

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

8 ) First copy of a Block moves it to heap, the rest are cheap

As implied by (7) and (2), that Blocks start on the stack can be a source of subtle bugs. Careful. The reason why Blocks start on the stack is because it is really really fast. Orders of magnitude faster than allocating a chunk of memory for each Block.

The first Block_copy() or -copy of a Block will do the allocation, move the Block to the heap, and update any __block variables innards to point to the new location.

After the first copy, Block_copy() and -copy are very fast; they effectively -retain the block. This works because Blocks are immutable.

It bears repeating; if you use -copy (or -retain after the first -copy), balance with -release. If you use Block_copy(), balance with Block_release().



10 Responses to “^ Blocks Tips & Tricks”

  1. John C. Randolph says:

    So, it seems to me that since blocks are allocated on the stack, and they’re executable, using them means the stack can’t be in memory that’s marked as non-executable. Is this a security hazard, or are the classic stack-smashing attacks a non-issue these days?

    -jcr

  2. yuji says:

    No, only the context (captured local variables, etc.) of the block is on the stack. The code is still on the executable section. The context on the stack only has a pointer to the said code.

  3. John C. Randolph says:

    Thanks, yuji. I wasn’t clear on that.

    -jcr

  4. Joachim Bengtsson says:

    As an addition to #2 and #5 is the case of storing a block in a collection like NSArray or NSDictionary longer than the block will survive on the stack; since they all use -[retain], the block needs to be moved to the heap first, like so:

    NSArray *thingsToDo = [NSArray arrayWithObjects:
    [[^ { NSLog(@"Hello"); } copy] autorelease],
    [[^ { NSLog(@"World!"); } copy] autorelease],
    nil];

    I write about that and more in my own blocks guide over at http://thirdcog.eu/pwcblocks/ :)

  5. Ben Holt says:

    One potential gotcha regarding recursive blocks (example 7): if the block might be copied for any reason or could outlive the scope of the current stack frame, you must store a copy of the block in your reference variable.

  6. Jonathan Lehr says:

    Thanks for the great article! One minor note: The Grand Central Dispatch technology brief linked at the top appears to have been superseded by a later version, and because the date is incorporated in the filename the older link doesn’t work anymore. Here’s a link to the updated file:

    http://images.apple.com/macosx/technology/docs/GrandCentral_TB_brief_20090903.pdf

  7. Andrew Hershberger says:

    Thanks for the great post. One question though: do blocks retain or copy heap-allocated blocks to which they refer? If so, why am I seeing the behavior described here: http://stackoverflow.com/questions/5446187/should-a-block-literal-retain-referenced-heap-allocated-blocks

  8. Tom says:

    So presumably if I had the code:

    - (void)someFuncWithCompletionHandler:(HandlerBlockType)handler
    {
    dispatch_async(,
    ^{
    // do stuff here
    handler(results or whatever)
    })
    }

    I’d need to copy ‘handler’? Blocks retain objects within them but do they spot the special case of other block objects?

  9. [iOS] blocks or selectors or delegates | DevMaster.vn says:

    [...] (note: the onTap property must ‘copy’ the block for it to work properly) [...]

  10. Alex says:

    I just run this code:

    typedef int(^Blocky)(void);
    Blocky b[3];

    for (int i=0; i<3; i++)
    b[i] = ^{return i;};

    and output was 0 1 2, not 2 2 2

    [bbum here: the compiler behavior has changed since this was written. I believe if you disable ARC, you'll see the original behavior.]

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>