在 ARC 下,在这种情况下或在大多数其他情况下,您不再需要手动复制块。根据clang's ARC documentation on blocks
除了作为初始化 __strong 参数变量或读取 __weak 变量的一部分完成的保留之外,只要这些语义要求保留块指针类型的值,它就会具有 Block_copy 的效果。当优化器发现结果仅用作调用的参数时,它可能会删除此类副本。
换句话说,大多数情况下,保留块具有 Block_copy 的效果,正如您所建议的那样。特别是,当块被添加到集合中时,它会被复制! (更准确地说,它已经在堆上。)下面是一些示例代码,证明了这种情况。
#import <Foundation/Foundation.h>
int main(int argc, char *argv[])
{
@autoreleasepool
{
NSMutableArray *arr = [[NSMutableArray alloc] init];
int counter = 0;
int total = 5;
for (int i = 0; i < total; i++) {
void (^block)(void) = ^{
NSLog(@"in this block, counter is %d", counter);
};
[arr addObject:block];
counter += 1;
}
for (int i = 0; i < total; i++) {
void (^block)(void) = arr[i];
block();
}
}
}
如果使用默认编译器(Apple LLVM 编译器 4.2)在最新的 Xcode (4.6.3 (4H1503)) 上运行,输出将是
in this block, counter is 0
in this block, counter is 1
in this block, counter is 2
in this block, counter is 3
in this block, counter is 4
如果这些块没有被复制到堆中,你会(可能——这是未定义的行为)看到
in this block, counter is 4
in this block, counter is 4
in this block, counter is 4
in this block, counter is 4
in this block, counter is 4
因为添加到数组中的指针都指向堆栈分配(非复制)块,该块在执行块时捕获了counter 值4。
尤其是,如果您禁用 ARC,您将获得相同的行为。即使您在将块添加到数组之前在块上调用retain
[arr addObject:[block retain]];
您仍然会得到相同的(“损坏的”)输出,说明这是一种 ARC 行为,而不是一般的保留行为。
注意:
ARC的retain没有Block_copy作用的两个地方是
(1) 作为初始化__strong 参数变量的一部分保留完成
在进入一个函数(或方法)后,如果一个对象被传递给该函数(或方法),该对象将被保留(堆栈中有对该对象的强引用)框架)并在函数(或方法)退出时释放(对对象的强引用被释放)。
这对于方块和任何其他对象一样都是正确的。 clang 文档中的这句话意味着即使在这种情况下保留了该块,该块也不会被此保留复制。
(2) 作为读取__weak 变量的一部分保留完成
类似地,当__weak 变量被读入__strong 变量时,会创建对该对象的强引用,并保留该对象。
块也是如此。但是,以这种方式发送的块不会被复制(由于将__weak 引用读入__strong 引用)。
这两个异常中的任何一个都会给您带来问题的情况很少见。一般来说,您不必担心复制块。