【问题标题】:Why is it not safe to use dispatch_block_t like this?为什么像这样使用 dispatch_block_t 不安全?
【发布时间】:2019-09-06 06:32:04
【问题描述】:

为什么这样使用 dispatch_block_t 不安全?

我在看dispatch_block_t的官方评论,发现如下代码,不明白哪里错了?为什么不安全?有人可以告诉我吗?我很感激。希望能详细给我解释一下。

#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__

摘自上述代码:

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

我不明白有什么问题?为什么不安全?

【问题讨论】:

    标签: ios c objective-c block grand-central-dispatch


    【解决方案1】:

    这里的文档有点混乱,因为它没有针对 ARC 进行全面更新。 Under ARC, this is all done for you automatically.

    但至于为什么需要它,Objective-C 块最初是在堆栈上分配的。这可以在立即使用和丢弃它们的情况下提高性能。如果它们脱离当前堆栈范围,则需要将它们复制到堆中,并像对象一样管理内存。这是使用Block_copy() 或传递-copy 手动完成的(ARC 之前的)。有关更多信息,请参阅Blocks Tips & Tricks

    但是你的直觉是正确的;只要您使用 ARC,发布的代码就可以了。如果它是当前 SDK 的一部分,您可能需要打开 Apple 反馈以请求更新此标头以使其更加清晰。

    【讨论】:

    • 那么,现在写这个安全吗?既然 ARC 帮我们把块复制到了堆里,我理解对了吗?@Rob Napier
    • 正确。如果块超出当前堆栈范围,ARC 会将块复制到堆中。
    • 谢谢,但我还是不太明白你所说的“如果它超出了当前堆栈范围”是什么意思。另外,你能看看我的另一个question吗?我正在学习如何制作 cocoapods。 XD @Rob Napier
    • 我不确定我是否可以在评论中充分解释堆栈:D 但是作为粗略的近似,局部变量存储在堆栈中并且仅在当前范围内可用(通常在当前功能)。当一个函数(大致)返回时,堆栈会丢失。超出该时间所需的变量必须在堆上分配,这要昂贵得多。见stackoverflow.com/questions/79923/…
    【解决方案2】:

    这是不安全的,因为不能保证块在它们所在的复合语句(即if 语句的“then”子句和“else”子句结束之后存在。问题类似如果你这样做会发生什么

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

    请注意,dispatch_async() 之类的函数会自动将块复制到堆中,因此只要在块超出范围之前调度块,就可以了。

    【讨论】:

      【解决方案3】:

      当不使用 Objective-C ARC 构建时

      在 ARC 中是安全的。而且我不知道你为什么会在 2019 年使用 MRC

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-23
        • 2013-03-10
        • 1970-01-01
        • 1970-01-01
        • 2019-10-27
        相关资源
        最近更新 更多