【问题标题】:Does ARC guarantee that an object is dealloc'ed instantly once the last strong reference is gone?ARC 是否保证在最后一个强引用消失后立即释放对象?
【发布时间】:2012-11-05 05:16:12
【问题描述】:

特别是,这种代码是否总是按预期工作(其中 MyResourceGuard 是一个在其 init 方法中锁定独占资源并在其 dealloc 方法中释放锁定的对象):

NSLog(@"About to capture some exclusive resource.");
{
  MyResourceGuard* guard = [MyResourceGuard new];
  // Do something with the exclusive resource here.
}
// guard is out of scope, therefore its dealloc should have
// been called right away and the resource should already
// be free again at this point.

我在书籍和博客中读过与例如Java 垃圾回收,ARC 会在引用计数减少到零时立即销毁对象(而不是在它自己方便的时候),但我没有在 Apple 的任何官方文档中读到这一点。如果这是真的,为什么我们需要 ARC 引入的新的 @autoreleasepool 关键字?

在调试过程中,我总是看到对象立即被释放,除非在 try-catch-block 中引发异常,在这种情况下,实际上 从未 调用了 dealloc(这是Mac 错误,或者只是这些可怕的 Objective C 怪事之一?)。

【问题讨论】:

  • “如果那是真的,我们为什么需要 ARC 引入的新的 @autoreleasepool 关键字?”因为仍然需要自动释放(因为您需要能够传递刚刚创建的对象,例如访问器),并且编译器需要明智地使用自动释放语义。
  • ...在 try-catch-block 中引发了一个异常,在这种情况下实际上从未调用过 dealloc -- ARC + ObjC 不会在这里救你。该对象会泄漏(除非 ARC 认为自动释放池是一个更好的地方)。 ARC + ObjC 在展开时不会生成清理代码。 ARC + ObjC++ 会做一些清理工作(不是全部)。最好避免 ObjC 中的异常 :)
  • 请注意,除了记录应用程序中的致命崩溃状态外,您不应将 try-catch 块用于任何事情;异常不应被视为可恢复的。
  • @Jesper:在 ARC 下,由于额外的外部引用,将对象传递出访问器会增加引用计数。一旦该外部引用不再存在,该对象将被释放;与自动释放池无关。
  • @AndreasZollmann:假设两端都是 ARC,是的,感谢objc_retainAutoreleaseReturnValue/objc_retainAutoreleasedReturnValue 相互作用。如果任何目的是手动保留/释放,你就不能做出这样的假设,所以自动释放是必要的。

标签: objective-c ios macos xcode4 automatic-ref-counting


【解决方案1】:

没有。如您的示例所示,您没有基于确定性范围的 ObjC 对象的破坏。

例如,这个程序可能会导致死锁:

{ MyResourceGuard* guard = [MyResourceGuard new]; }
{ MyResourceGuard* guard = [MyResourceGuard new]; }

如果您需要此功能,最好的办法是使用 C++ 类型 (SBRM, RAII) -- 在 Objective-C++ 中也可用(但不适用于 objc 对象)。

它接近了,但您只需要等到引用计数达到零才能调用-dealloc,这就是保证关闭的原因(通常!=总是)。这个问题实际上与您永远不会依赖或使用-retainCount(如果可用)的原因非常相似。示例:自动释放池、异常、更改运行时或 ARC 生成的代码、编译器优化、使用具有不同代码生成标志的实现可能会导致 objc 对象的生命周期延长超出范围。

更新

whole page on ARC at clang's site 是一个很好的阅读主题——包括细节、保证(和缺乏保证),但特别是:

6.1。精确的生命周期语义

通常,ARC 维护一个不变量,即可保留对象 保存在 __strong 对象中的指针将被保留用于完整的正式 对象的生命周期。受此不变量约束的对象具有精确的 生命周期语义。

默认情况下,自动存储时长的局部变量没有 精确的生命周期语义。这样的对象只是强引用 它保存可保留对象指针类型的值,以及这些值 仍然完全受到局部值的优化 控制。

基本原理:严格应用这些精确生命周期语义将是 禁止的。许多有用的优化,理论上可能 减少对象的生命周期将是不可能的。 从本质上讲,它承诺太多了。

可保留对象所有者类型和自动存储的局部变量 持续时间可以用 objc_precise_lifetime 属性注释到 表示它应该被认为是一个精确的对象 生命周期语义。

理由:尽管如此,有时能够强制 对象要在准确的时间释放,即使该对象没有 似乎被使用。这可能并不常见,以至于 显式请求这些语义的句法权重不会 繁琐,甚至可以使代码更清晰。

即使您确实使用了objc_precise_lifetime 属性,它也会应用于该强本地变量 的引用计数操作——而不是对象的生命周期。

【讨论】:

  • 感谢您的回答。为什么由于自动释放池而在 ARC 下发布会被推迟?目前是否存在任何可能导致延迟的编译器优化?关于代码生成,我假设您指的是意外生成非 ARC 代码?
  • a) 这是自动释放池的用途。对象被添加到池中(当自动释放或添加引用它们的对象时)。然后在销毁池时发送所有释放消息。 b)您还必须考虑运行时。即使您尝试,也无法保证 - 很快就会用参考更新答案。 c) 不。编译器使用少数 C 函数来执行引用计数。确切的细节,以及运行时如何处理它,可能会有所不同。现在最明显的例子是 MRC 和 ARC 之间的区别(续)
  • (cont) 示例:ARC 能够将返回值的引用传递给调用者,而 MRC 则没有。也有 ARC 使用自动释放的情况。运行时甚至可以免费使用自动释放。编译器还保留了它认为合适的指令排序的能力。语言、运行时或编译器都不能保证精确的生命周期。因为您正在与几个抽象级别作斗争,所以在所有情况下,您都无法可靠地确定对象将被释放(或者甚至发送消息release,就此而言)的序列点。
  • @AndreasZollmann 做出了一些保证——如果您对 ARC 有较低的了解,您会喜欢这个:clang.llvm.org/docs/AutomaticReferenceCounting.html
【解决方案2】:

这种代码将始终有效。 (或者至少只要 ARC 像现在这样工作) ARC中对象生命周期的一个小例子:

{
    NSString *a = [[NSString alloc] init]; //object allocation
    a = nil; // object is deallocated as it was created in this scope

    NSString *b = [NSString string]; //method call which returns an object (always autoreleased)
    b = nil; // object is not deallocated as it was not created in this scope and is therefore autorelased
}

【讨论】:

    猜你喜欢
    • 2014-11-26
    • 2013-12-12
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-22
    • 2012-04-10
    • 1970-01-01
    相关资源
    最近更新 更多