2

Why is it not safe to use dispatch_block_t like this?

I am reading the official comment of dispatch_block_t, I found the following code, I don't understand what is wrong? Why is it not safe? Can someone tell me? I am very grateful. I hope to explain to me in detail.

#ifdef __BLOCKS__
/*!
 * @typedef dispatch_block_t
 *
 * @abstract
 * The type of blocks submitted to dispatch queues, which take no arguments
 * and have no return value.
 *
 * @discussion
 * When not building with Objective-C ARC, a block object allocated on or
 * copied to the heap must be released with a -[release] message or the
 * Block_release() function.
 *
 * The declaration of a block literal allocates storage on the stack.
 * Therefore, this is an invalid construct:
 * <code>
 * dispatch_block_t block;
 * if (x) {
 *     block = ^{ printf("true\n"); };
 * } else {
 *     block = ^{ printf("false\n"); };
 * }
 * block(); // unsafe!!!
 * </code>
 *
 * What is happening behind the scenes:
 * <code>
 * if (x) {
 *     struct Block __tmp_1 = ...; // setup details
 *     block = &__tmp_1;
 * } else {
 *     struct Block __tmp_2 = ...; // setup details
 *     block = &__tmp_2;
 * }
 * </code>
 *
 * As the example demonstrates, the address of a stack variable is escaping the
 * scope in which it is allocated. That is a classic C bug.
 *
 * Instead, the block literal must be copied to the heap with the Block_copy()
 * function or by sending it a -[copy] message.
 */
typedef void (^dispatch_block_t)(void);
#endif // __BLOCKS__

Excerpt from the above code:

dispatch_block_t block;
if (x) {
    block = ^{ printf("true\n"); };
} else {
    block = ^{ printf("false\n"); };
}
block(); // unsafe!!!

I don't understand what is wrong? Why is it not safe?

ink
  • 519
  • 6
  • 19

3 Answers3

2

The documentation here is a little confusing, since it wasn't fully updated for ARC. Under ARC, this is all done for you automatically.

But as to why it's required, Objective-C blocks are initially allocated on the stack. This improves performance in cases where they are immediately used and discarded. If they escape the current stack scope, then they need to be copied to the heap and memory managed like an object. This is done manually (pre-ARC) using Block_copy() or passing -copy. See Blocks Tips & Tricks for more on that.

But your intuition here is correct; the posted code is fine as long as you're using ARC. You may want to open an Apple Feedback to request this header be updated to be more clear if it's part of the current SDK.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • So, is it safe to write this now? Since ARC has helped us copied the block to the heap, I understand it right?@Rob Napier – ink Sep 07 '19 at 11:45
  • Correct. ARC will copy the block to the heap if it escapes the current stack scope. – Rob Napier Sep 07 '19 at 13:08
  • Thank you, but I still don't quite understand what you mean by "if it escapes the current stack scope". Also, can you look at my other [question](https://stackoverflow.com/questions/57833676/loading-resource-bundle-error-in-cocoapods)? I am learning how to make cocoapods. XD @Rob Napier – ink Sep 07 '19 at 13:20
  • I'm not sure if I can adequate explain the stack in a comment :D But as a rough approximation, local variables are stored on the stack and are only available in the current scope (generally within the current function). When a function returns (roughly), the stack is lost. Variables needed beyond that time have to be allocated on the heap, which is much more expensive. See https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap – Rob Napier Sep 07 '19 at 15:05
1

It is unsafe because the blocks are not guaranteed to exist after the end of the compound statement that they are in (i.e, the “then” clause and the “else” clause of the if statement. The problem is similar to what happens if you do

char *fun(void) {
    char str[] = "hello";
    return str; // !!! returning a pointer to an array
                // about to go out of scope!
}

Notice that functions like dispatch_async() automatically copy the block to the heap, so as long as you dispatch the block before it goes out of scope, you’re fine.

Ture Pålsson
  • 6,088
  • 2
  • 12
  • 15
0

When not building with Objective-C ARC

It's safe in ARC. And I don't know why you will use MRC in 2019

Cy-4AH
  • 4,370
  • 2
  • 15
  • 22