块在堆栈上开始生命,因此,块的生命周期仅与声明它的范围一样长。
for() 循环的主体——{} 中的循环主体——本身就是一个范围。因此,您的代码将对堆栈 [块] 上的某些内容的引用放入周围范围 [语言数组] 中的变量中。
您需要将块复制到堆以使其生存:
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = [^{ printf("hello, %d\n", i); } copy];
}
}
如果不使用 ARC,您还需要-release
在某些时候复制块。
您可能会发现这篇博客文章很方便(我是在 Blocks 公开后不久写的)。这篇文章介绍了一些技巧、窍门和陷阱。
等等——是的——你是对的。ARC 编译器中发生了一些神奇的事情,导致块似乎神奇地在堆上。但是,我在 LLVM 文档中找不到任何明确记录此行为的内容。如果关闭 ARC,您会看到输出类似于 2,2,2 而不是 0,1,2。
这是一些新的行为。我不会依赖这种行为,直到有人可以在编译器中找到明确定义如何支持它的明确注释。
@autoreleasepool {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
void (^block)(void) = ^{ printf("hello, %d\n", i); };
NSLog(@"%p", block);
blockArray[i] = block;
NSLog(@"%p", blockArray[i]);
}
for (int i = 0; i < 3; ++i) blockArray[i]();
}
输出:
2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2
因此,块是在堆栈上创建的,并在 for() 循环范围之外的赋值时自动复制到堆中。
一个类似的测试还表明,当作为参数传递给 NSArray 的 addObject: 时,该块将被复制。