Search code examples
ioscobjective-cblockgrand-central-dispatch

Why is it not safe to use dispatch_block_t like this?


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?


Solution

  • 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.