【问题标题】:Why does ARC copy this block to the heap?为什么 ARC 将此块复制到堆中?
【发布时间】:2013-10-05 18:45:18
【问题描述】:

考虑这个简单的例子

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

如果没有 ARC,上面的代码会打印出来

Class: __NSStackBlock__

而使用 ARC 可以打印

Class: __NSMallocBlock__

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

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

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

【问题讨论】:

  • 对我来说,这看起来像是 Why is this block not global? 的副本,你同意吗? - (我没有发现接受的答案完全令人满意,但也许没有更好的答案,或者它只是一个编译器错误。)
  • 感谢您指出这一点,但不,不是真的。这个问题是关于为什么该块没有被编译为全局的,而我想知道为什么 ARC 会插入 Block_Copy()
  • 请注意,我特意通过引用周围范围来确保该块不是全局的。
  • 好吧,我不是汇编代码方面的专家,但如果我正确理解了该问题的文本,则会在此处插入一个意外的“块副本”,将全局块移动到堆中。 - 如果将int i = 42 替换为const int i = 42,则会得到一个全局块,这与引用问题的第二个示例中的行为相同。
  • 一个全局块已经在堆上,不是吗?无论如何,我明白你的意思,但我仍然相信这与编译器误解的块类型无关。在这种情况下,块类型是已知的(它确实是一个堆栈块,正在被复制到堆中)。问题是:为什么 ARC 复制堆栈块?感谢 Catfish_Man 的评论,我知道在哪里看,我做了功课,并试图在下面回答。让我知道你的想法

标签: objective-c automatic-ref-counting objective-c-blocks


【解决方案1】:

ARC 将块指针类型视为retainable object pointers types,并且根据documentation,此类类型(在没有显式所有权限定符的情况下)被隐式假定为具有__strong qualifiers

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

所以上面的例子等价于

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

documentation 还声明:

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

later

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

的效果

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

跳过赋值会将块保留在堆栈中。考虑以下示例:

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

documentation 也告诉我们

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

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

【讨论】:

  • 感谢分享,很有帮助。
猜你喜欢
  • 2013-02-11
  • 2021-10-20
  • 1970-01-01
  • 2020-12-04
  • 2022-11-28
  • 2019-12-23
  • 2018-08-29
  • 2014-08-30
  • 1970-01-01
相关资源
最近更新 更多