Returning block that lives on the local stack

2 Solutions Collect From Internet About “Returning block that lives on the local stack”

You need to make a copy of the block to move it to the heap.

i.e. something like:

dispatch_block_t createPrintBlock (const char *msg) {
    return Block_copy(^{
        printf("%s", msg);
    }) ;

The error means you are returning a value which will be invalid after the method returns. This is not just an issue with blocks, consider:

- (int *) badMethod
   int aLocalIntVariable;

   return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die...

Local variables are created when a method is entered, the place where they live is called the “stack”. When a method returns those local variables are destroyed. You can return a value in such a variable, but you cannot return a reference to the variable itself – it will be invalid. You can pass a reference to a local variable to a method you call, as your local variable still exist in that case.

In your case you have created a block. Objective-C happens to create block values on the stack, i.e. effectively in an anonymous local variable, and refer to them using a reference. You can pass such a reference to a method you call, but you cannot return it – that anonymous local variable is destroyed just like any other.

However Objective-C provides you two ways to create a copy of a block value as an object, which lives on the “heap”, and which will outlive its creator. First there is Block_copy which is a function:

<reference to heap allocated block> = Block_copy(<reference to stack allocated block>);

This is the original way to do this and is supported every – including in pure C code, blocks are part of C and not just Objective-C. The second way pretends the block is an object already and allows you to send the standard copy message:

<reference to heap allocated block> = [<reference to stack allocated block> copy];

If you are primarily an Objective-C person this second method probably feels more comfortable, but does blur the issue of why it is needed in the first place.

ARC helps here, in automating memory management it will automatically copy blocks from the stack to the heap (at least in current compilers, it may not work properly in earlier compilers) and so the programmer can ignore what is really an implementation detail.

Addendum: ARC

The last paragraph above was queried by @newacct and a long Q&A comment exchange resulted. In an attempt to make the information in that easier to follow we have both removed our comments and I have consolidated the information here as an addendum.

In understanding how ARC handles blocks two documents are useful:

  1. Objective-C Automatic Reference Counting, in particular sections 3 (blocks are retainable object pointers), 3.2.3 (retainable object types are valid across return boundaries) and 7.5 (rules for when blocks are copied).
  2. Transitioning to ARC Release Notes, in particular the FAQ item “How do blocks work in ARC?”

From these it can be determined that most of the time ARC will handle all copying of blocks from the stack to heap as part of its management of all object types.

The second reference highlights one case, that at least at the time the document was written, was not handled automatically. That case is where a stack allocated block is passed to a method parameter of type id, e.g. something like:

- (void) takeBlockWithTypeLoss:(id)block { ... }

[obj takeBlockWithTypeLoss:^{ ... }];

In such cases, at the time the document was written, ARC did not copy the block. If the called method then performs an operation which retains the passed block a problem occurs as the retain value is not on the heap. (Note that the block needs to be stack allocated for a problem to occur. Literal blocks which do not reference variables in their environment are statically allocated, also a literal block which is first stored in a local variable with default strong ownership and then passed to the method will be copied.)

This case is an example of type loss, a value known to be a block type is passed as id loosing type information. The compiler can always determine such points, so why does (or did..) ARC not copy the block? The answer given in the past is simply one of efficiency, the copy may not be required and lots of unneeded copies is a performance hit.

However the current compiler (Xcode 4.6.1) appears to handle this one remaining case, at the point of type loss a block is copied to the heap. If anyone can show this is now documented (or you are confident that your compiler handles this case e.g. by coding a check) then it would appear Block_copy() (or [block copy]) can be relegated to history, if not then when type loss occurs it should be used.

Addendum: June 2013

As revealed by this question there is a case that Xcode 4.6.3/Clang 4.2 does not handle. When a block is passed as one of the variable arguments to a variadic method then the compiler does not automatically promote a stack block to the heap. This is a subcase of type loss mentioned above.

So there is a case current compilers do not handle, which suggests a reason why the compiler supporting more than the specification is undocumented – that support is incomplete (though these is no theoretical reason that it needs to be).

So as before, if there is type loss then the compiler may not handle block promotion automatically (but this can be tested for if so desired), cases not involving type loss are handled automatically as per the specification.

(BTW. The older question mentioned in a comment to the question above is now one of the cases covered by the specification and handled correctly by the compiler.)