C w / Blocks:基于堆栈的块超出范围

时间:2011-09-16 01:36:10

标签: c objective-c-blocks grand-central-dispatch

在Apple的libdispatch queue.h标题文件中,出现以下警告:

// The declaration of a block allocates storage on the stack. 
// Therefore, this is an invalid construct:

dispatch_block_t block;

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

// What is happening behind the scenes:

if (x) {
    struct Block __tmp_1 = ...; // setup details
    block = &__tmp_1;
} else {
    struct Block __tmp_2 = ...; // setup details
    block = &__tmp_2;
}

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

尽我所能,我无法想出一个例证这个bug的测试用例。我可以创建在堆栈上实例化的块,但它们(似乎)总是出现在堆栈上的唯一地址,即使在相互之间超出范围时也是如此。

我想这个问题的答案很简单,但它逃脱了我。任何人都能填补我(有限)理解的空白吗?

编辑:我看过this响应,但我不太明白该实例如何转换为我上面发布的示例。有人可以使用if构造向我展示一个示例吗?

1 个答案:

答案 0 :(得分:5)

为了使函数内的堆栈闭包崩溃:

  • 您需要确保闭包确实是堆栈闭包。从Apple Clang 2.1开始,在当前上下文中不引用变量的闭包(如queue.h中的变量)被实现为全局闭包。这是一个实现细节,可以在不同的编译器/编译器版本之间变化;

  • 编译器必须发出有效重用/重写闭包曾经存在的堆栈区域的代码。否则,该函数内的每个对象都存在于函数堆栈框架中的不同地址中,这意味着您不会在该函数内部发生崩溃。似乎Apple Clang 2.1没有重用堆栈内存地址。 GCC 4.6可以重用它们,但它不支持闭包。

由于Apple Clang 2.1没有在函数堆栈框架中重用地址而且GCC 4.6不支持闭包,所以我可以告诉它不可能制作这个特定的例子 - 在函数内部调用超出范围的堆栈关闭 - 崩溃。

我在my blog上写了一篇关于此问题的更详细的文字。