0

我敢打赌,我可以通过阅读此处的类似主题或通过谷歌搜索找到这个问题的答案,但我想直接“听到”它可以这么说,因为这是我理解的反常现象。

所以事情是这样的,我有一些以前的员工写的代码,我看到很多特定类型的结构,这对我来说看起来很奇怪,我只想澄清什么是“对与错”。

例如

- (void) setWwanServiceId: (NSString *) newValue {
    [wwanServiceId autorelease];
    wwanServiceId = [newValue copy];
}

wwanServiceIdNSString班级的一名成员,对我来说,这似乎是一种奇怪的方式。据我了解,它首先会autorelease在对象上放置一个,基本上是说:“每当这个对象似乎不被使用时,为我释放它我并不在乎”然后副本将保留计数 +1 .... wwanServiceId?还是新价值?我猜是第一个

然后让我更加困惑,让我们快速浏览一下wwanServiceId-string 的生命周期..

基本上,如果我们收到一个通知,则该值将被设置,然后通知处理程序方法将调用上述-setWwanServiceId:方法。除此之外,它只会被访问以供阅读,所以我们可以肯定地说它会在任何给定点:

  1. 将自动释放放在对象上
  2. 保留一个新的字符串副本

然后还有一个怪癖,这就是我越来越怀疑的地方,即-dealloc看起来像这样的方法:

- (void) dealloc {
    [[self wwanServiceId] release];
    [super dealloc];
}

那么那里会发生什么?正如我所说,它releasewwanServiceId内存管理唯一一次被触及(如果我没有错过任何东西,但我很确定)是放置一个自动释放并保留它。

所以总结一下:这里的想法是他认为因为他总是在放置自动释放后保留一个新副本,所以他需要在最后释放它..或者这是我唯一能想到的。或者只是觉得最后做一个额外的发布是安全的,以防万一..?

因为据我了解,如果此设置器被调用一次,它将放置一个autorelease(-1 将来),执行一个retain(+1),当调用析构函数时,它将执行“最终释放”(-1)。

任何帮助我理解的想法或建议(如果事实上我错了并且内存处理是正确的)将不胜感激。

4

4 回答 4

2

你写了:

这里 wwanServiceIdis 是类的 NSString 成员,对我来说这似乎是一种奇怪的方式。据我了解,它将首先在对象上放置一个自动释放,基本上是说:“每当这个对象似乎不被使用时,为我释放它我并不在乎”然后副本将保留计数+1 on.... wwanServiceId?还是新价值?我猜是第一个

这似乎指出了您困惑的根源。wwanServiceId是一个变量,可以包含对类型对象的引用NSString。变量没有引用计数,只有对象才有。

前员工写道:

- (void) setWwanServiceId: (NSString *) newValue {
   [wwanServiceId autorelease];
   wwanServiceId = [newValue copy];
}

该表达式的[wwanServiceId autorelease]意思是:读取存储在其中的引用wwanServiceId并自动释放该引用所引用的对象 - 我们称该对象为 A。重要提示:这不会删除对象 A;它将在release以后的某个阶段被删除,如果那时没有剩余的引用,对象 A 将被删除。

该表达式的[newValue copy]意思是:读取存储在 中的引用newValue,使用它来定位对象(称为对象 B),复制该对象以生成新对象(称为对象 C),并返回对新对象的引用。这个新对象归 的调用者所有copy,因此不需要retain它。

最后,分配将对对象 C 的引用存储到wwanServiceId.

因此,最多涉及三个不同的对象:

  1. A: 引用的原始对象wwanServiceId,这是自动释放的以删除wwanServiceId.
  2. B: 引用的对象newValue,这个保持不变
  3. C:新创建的 B 的副本,通过wwanServiceId

为什么代码中的“自动释放”和上面的“最多三个不同”?

该方法可以通过newValue引用对象 A 来调用,例如:

[self setWwanServiceId:[self wwanServiceId]]

如果发生这种情况并且 (a)release被使用而不是autorelease(b) 没有其他对对象 A 的引用,那么release将删除对象 A,然后[newValue copy]评估时newValue将引用已删除的对象...autorelease在此使用case 将删除延迟到复制之后。

因此,前任员工所写的内容绝不是“错误的”,但正如其他一些答案所暗示的那样,这可能是一种不寻常的风格。你看到这个写的另一种方式是:

- (void) setWwanServiceId: (NSString *) newValue
{
   NSString *oldValue = wwanServiceId;
   wwanServiceId = [newValue copy];
   [oldValue release];
}

这也确保在复制之后发生任何删除。

HTH。

于 2012-08-30T10:41:10.690 回答
1

简短而有帮助:

这是错误的:

- (void) setWwanServiceId: (NSString *) newValue {
    [wwanServiceId autorelease];
    wwanServiceId = [newValue copy];
}

这是正确的:

- (void) setWwanServiceId: (NSString *) newValue {
    if (newValue != wwanServiceId) {
        [wwanServiceId release];
        wwanServiceId = [newValue copy];
    }
}

简而言之:

[wwanServiceId autorelease];是一个不必要的发送消息,因为自动释放一个对象会减少未来某个未知点的保留计数。在下一行中,您将wwanServiceId = [newValue copy];立即设置实例变量。因此,在您的记忆中,您现在有一个要自动释放的对象和一个新对象。其中之一是太多新对象是您的 IVar 的指针所指向的位置。旧的在你的记忆池中游泳,可能没有参考它:-)

尽可能少地自动释放或使用 ARC。

哦:在dealloc方法中,请不要这样发送消息:

[[self wwanServiceId] release];

最好像这样:

[wwanServiceId release];

正如 Apple 建议的那样,直接使用 and 方法中的实例方法initdealloc而不是在那里使用 getter 和 setter。

于 2012-08-30T09:34:05.723 回答
0

调试一下看看。

[wwanServiceId autorelease];

wwanServiceId 有一个地址。该声明不会改变它。它确实减少了这个对象的保留计数。而已。

wwanServiceId = [newValue copy];

此语句创建一个新对象。新对象是 newValue 的副本。比较对象的地址,您会看到,地址 inn wwanServiceId 将与 newValue 的地址不同,并且与 wwanServiceId 在语句执行前的地址不同。

隐含的保留copy会影响 wwanServiceId,但它会影响刚刚使用copy. 它不影响之前语句中自动释放的 wwanServiceId 对象。

在执行setWwanServiceId完成后的某个时刻,旧的和自动释放的对象将消失。(假设现在保留计数为 0。如果它 > 0 是因为它仍因其他原因或仅出于错误而保留,那么它将保留并可能导致泄漏。)

一旦你明白了,你就不会再质疑 dealloc 方法中发生了什么。wwanServiceId被释放。这意味着它的保留计数减少了 1。如果它是 0,那么它将被自动释放。你甚至可以在那里自动释放它。与自动释放的区别基本上是在当前方法正在执行时,自动释放的对象仍然存在并且可用。它的发布将在稍后的某个时间点生效。

但是没有理由在 dealloc 中自动释放对象。在给出的示例中,甚至没有充分的理由在 setter 中自动释放对象setWwanServiceId。您可以直接在这两种方法中释放对象。

于 2012-08-30T08:40:47.313 回答
0

假设是由 getter和 setterwwanServiceId映射的私有 ivar ,我认为在 setter 中自动释放 ivar 是不正确的。我会编写以下代码:wwanServiceIdsetWwanServiceId

- (void) setWwanServiceId: (NSString *) newValue {
    if (newValue != wwanServiceId) {
        [wwanServiceId release];
        wwanServiceId = [newValue copy];
    }
}

我的意思是:没有必要将您的 var 的所有权授予自动释放池(它可能在应用程序结束时被耗尽)。只需释放 ivar。如果有人在使用它,没问题,它会对它有很强的引用。否则它将被释放。

于 2012-08-30T09:09:19.057 回答