在 ARC下,在这种情况下或在大多数其他情况下,您不再需要手动复制块。根据clang 的关于块的 ARC 文档
除了作为初始化 __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) 保留 done 作为初始化__strong
参数变量的一部分
在进入一个函数(或方法)后,如果将一个对象传递给该函数(或方法),该对象将被保留(堆栈帧中对该对象的强引用)并在该函数(或方法)时释放) 退出(对该对象的强引用被释放)。
这对于块和任何其他对象都是如此。clang 文档中的这句话意味着即使在这种情况下保留了该块,该保留也不会复制该块。
(2) 作为读取__weak
变量的一部分保留完成
类似地,当一个__weak
变量被读入一个__strong
变量时,会创建一个对该对象的强引用,并保留该对象。
块也是如此。但是,以这种方式发送的块不会被复制(作为将__weak
引用读入引用的结果)。__strong
这两个异常中的任何一个都会导致您出现问题的情况很少见。通常,您无需担心复制块。