10

考虑这个简单的例子

int i = 42;
int (^aBlock)() = ^ {
    return i;
};
NSLog(@"Class: %@", [aBlock class]);

没有 ARC 上面的代码打印

Class: __NSStackBlock__

而使用 ARC 它会打印

Class: __NSMallocBlock__

我放置了一个符号断点,_Block_copy看起来 ARC 正在插入一个Block_Copy()调用,导致块被移动到堆中

这似乎是不必要的开销,它首先破坏了在堆栈上放置块的整个目的。

这是 ARC 的限制还是设计选择?

4

1 回答 1

13

块指针类型被 ARC 视为可保留的对象指针类型__strong qualifiers,并且根据文档,此类类型(在没有显式所有权限定符的情况下)被隐式假定为具有:

如果一个对象声明为具有可保留对象所有者类型,但没有显式所有权限定符,则其类型被隐式调整为具有 __strong 限定符。

所以上面的例子等价于

int i = 42;
__strong int (^aBlock)() = ^ {
    return i;
};
NSLog(@"Class: %@", [aBlock class]);

文档还指出:

对于__strong对象,首先保留新的pointee;其次,左值加载了原始语义;第三,新的指针对象以原始语义存储到左值中;最后,旧指针被释放。

后来_

[...] 当这些语义要求保留块指针类型的值时,它具有 Block_copy [...]

Block_copy所以是的,ARC会在将块分配给变量时引入调用,因为变量被隐式假定为__strong-qualified。

跳过分配将使块保留在堆栈上。考虑以下示例:

NSLog(@"Class: %@", [^int{return i;} class]); // => Class: __NSStackBlock__

文档还告诉我们,

当优化器发现结果仅用作调用的参数时,它可能会删除此类副本。

确实如此。正如 Catfish_Man 所提议的,打开优化(在这种情况下通过使用 Release 构建配置)将剥离Block_Copy调用,将块留在堆栈上。

于 2013-10-05T19:21:56.600 回答