我在“Effective Objective-C 2.0”一书中找到了一个块示例
void (^block)();
if (/* some condition */) {
block = ^ {
NSLog(@"Block A");
};
} else {
block = ^ {
NSLog(@"Block B");
};
}
block();
代码很危险,以下是书中的解释:
在
if
和else
语句中定义的两个块在堆栈内存中分配。当它为每个块分配堆栈内存时,编译器可以在分配内存的作用域末尾覆盖此内存。因此,保证每个块仅在其各自的if
- 语句部分中有效。代码将编译时没有错误,但在运行时可能正常或可能无法正常运行。如果它没有决定生成覆盖所选块的代码,代码将运行而不会出错,但如果确实如此,则肯定会发生崩溃。
我不明白“如果它没有决定生成覆盖所选块的代码,那么代码就可以正常运行,但如果确实如此,那肯定会发生崩溃。”
有人可以解释并举例说明吗?
答案 0 :(得分:1)
问题类似于在函数本地创建的C数组,然后在函数返回后使用:
#import <Foundation/Foundation.h>
dispatch_block_t global_block;
int * global_arr;
void set_globals(void)
{
if( YES ){
global_block = ^{
NSLog(@"Summer is butter on your chin and corn mush between every tooth.");
};
int arr[5] = {1, 2, 3, 4, 5};
global_arr = arr;
}
}
void write_on_the_stack(int i)
{
int arr[5] = {64, 128, 256, 512, 1024};
int v = arr[3];
dispatch_block_t b = ^{
int j = i + 10;
j += v;
};
b();
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
set_globals();
write_on_the_stack();
global_block();
NSLog(@"%d", global_arr[0]); // Prints garbage
}
return 0;
}
用于存储数组值的堆栈空间可以用于任何目的。我在这里使用单独的功能,因为它最可靠地证明了问题。对于您的确切情况,使用if
块和相同函数中的访问,编译器仍可以自由地重用堆栈空间。 可能不,但你不能依赖它。你打破了语言的范围规则(从C派生)。
正如Jesse Rusak和CrimsonChris在评论中指出的那样,在ARC 下编译了一个块类型变量,Block就像数组一样在堆栈上创建,但是从堆栈中复制(到堆)当它存储在强指针中时。默认情况下,所有对象指针(包括全局对象)都很强大。
如果您没有使用ARC编译,这将是不可靠的。我无法用我当前的编译器提出一个失败的例子,但同样,它违反了规则,编译器没有义务做你想做的事。
答案 1 :(得分:-2)
基本上这就是说,如果代码在单独的线程上运行,并且某些内容被分配给块当前使用但在块()之前的内存区域强烈的呼唤,那么坏事就会发生。
void (^block)();
if (/* some condition *)) {
block = ^ {
NSLog(@"Block A");
}
} else {
block = ^ {
NSLog(@"Block B");
}
}
<--- another thread overwrites the **block** block
block(); <--- runtime error since **block** has been dereferenced.