【问题标题】:Another "Retain, then Release" question另一个“保留,然后释放”问题
【发布时间】:2011-09-06 23:28:07
【问题描述】:

作为 Cocoa/Obj-C 的新手,我正在阅读 Aaron Hillegass 的“Mac OS X 的 Cocoa 编程”一书 - 撇开这样一个事实,即现在我们也有机会使用 GC 来避免所有这些推理 -我不确定我是否明白其中一些保留的原因。

特别是在 Aaron 给出的一个很好的编程实践示例中:

- (void) setFoo:(NSCalendarDate *)x
{
    [x retain];
    [foo release];
    foo = x;
}

我不明白在方法的第一行保留 x 实例的原因:

[x retain];

这个实例的作用域只是 set 方法,对吧? 退出方法范围时,无论如何都应该释放 x 实例,不是吗? 此外,当将 x 分配给 foo 时:

foo = x;

foo 无论如何都会指向 x 个内存单元,因此会增加指向的对象保留计数,不是吗?这应该确保内存不会被释放。

那么,有什么意义呢?当然,我确信我错过了一些东西,但不知道到底是什么。

谢谢, 法布里奇奥

【问题讨论】:

  • 附带说明,这里的代码实际上突出了一个好的实践。在某些情况下,“x”和“foo”可能已经指向同一个对象。在这种情况下,如果您遵循典型的 setter 流程​​ '[foo release],然后 foo = [x retain]',那么您将首先释放 'x'/'foo' (),然后再次保留它。如果在调用“setFoo:”时对象的保留计数为“1”,则意味着“x”/“foo”首先被释放,然后释放的对象被传递“保留”消息。而是按照 Aaron 的流程,在释放 'foo' 之前先保留 'x',避免了这个问题。

标签: objective-c cocoa memory-management retaincount


【解决方案1】:

保留的意思是:我需要这个对象留下来,它不能被释放。如果x不被保留,可能会发生以下情况:

您将x 分配给foo,因此foo 现在指向您的NSCalendarDate 所在的地址。有人释放或自动释放这个对象,它的保留计数最终下降到 0 并且对象被释放。现在你的foo 仍然指向那个地址,但是不再有一个有效的对象。一段时间后,创建了一个新对象,并且碰巧它与旧的 NSCalendarDate 对象位于同一地址。现在你的foo 指向一个完全不同的对象!

为防止这种情况,您需要retain 它。你需要说,请不要释放对象,我需要它。一旦你完成它,你release它意味着我不再需要这个对象,你如果没有其他人需要,现在可以清理它。

现在是经典的三部分作业。考虑你的setFoo: 看起来像这样:

- (void) setFoo:(NSCalendarDate *)x
{
    [foo release];
    [x retain];
    foo = x;
}

这是一个非常糟糕的主意。考虑您的对象是唯一保留 NSCalendarDate 对象的对象,并考虑您会这样做:[self setFoo:foo];。可能听起来很傻,但这样的事情可能会发生。现在的流程是这样的:

  1. foo 将被释放。它的保留计数现在可能会降至 0,并且该对象将被释放。
  2. 糟糕,我们正在尝试保留和访问已释放的对象。

这就是为什么你总是先retain新对象,然后release旧对象。

如果您来自 Java 或 .NET 背景,了解Foo * 类型的变量仅包含对象的地址 非常重要,仅此而已。在 Java 或 .NET 中,如果您愿意,指向对象的变量会自动“保留”它。在 Objective-C 中并非如此(在非 GC 环境中)。您可以将 Foo * 类型的变量视为弱引用,并且您需要明确告诉 Objective-C 在该地址您是否仍需要该对象。

【讨论】:

  • 感谢您的精彩解释 DarkDust 它消除了人们的疑虑,但我可能在我的问题中没有明确说明的唯一一点是:foo = x; 的行不是会增加保留计数对象 x 无论如何指向...?这就是为什么我认为强制保留毫无用处......
  • 哦,我刚刚又读了最后一部分关于弱引用的内容!这就是我的答案!我想知道为什么 Aaron 没有用粗体字指出这一点……这与我习惯的其他语言有很大的不同。谢谢:)
  • @Fabrizio Prosperi:也许亚伦没有强调这一点是因为他的背景。当他编写第一版时,.NET 还不存在,Java 还很年轻。当时大多数程序员都有 C 背景,或者可能是 Pascal 或其他过程语言。唯一值得注意的 GC 语言是 LISP。对于这些人来说,这个变量赋值只复制一个地址,不做任何其他事情行为是绝对自然的,不需要解释。只是随着过去几年 GC 驱动的语言/环境的兴起,这种情况发生了变化。
  • 今天有人向我提出了这个问题,我为 [foo setFoo:foo] 编写了一个测试函数,在 setFoo 中我不首先保留传递的值,我只是释放 foo 并将其分配给foo = [passedValue retain] 并且工作正常,没有内存问题...
  • @Shizam:我敢打赌,在你的情况下,该对象也被保留在其他地方。换句话说,当您运行 setFoo: 方法时,您的 foo 参数的保留计数不是 1。
猜你喜欢
  • 2011-11-17
  • 1970-01-01
  • 2012-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
相关资源
最近更新 更多