基本的内存管理混乱

时间:2012-08-30 08:00:22

标签: objective-c memory-management

我敢打赌,我可以通过阅读类似的帖子或通过谷歌搜索来找到这个问题的答案,但我希望第一手“听到”它可以说,因为这是我理解中的一个异常。

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

例如

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

此处wwanServiceId是该班级的NSString成员,对我而言,这似乎是一种奇怪的方式。据我所知,它会首先在对象上放一个autorelease,基本上说:“每当这个对象好像不被使用时,为我发布它我并不在乎”然后副本就会出现保留计数+1 .... wwanServiceId?还是newValue?我想是第一个。

然后让我更加困惑让我们快速浏览wwanServiceId的生命周期 - 字符串..

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

  1. 在对象上放置autorelease
  2. 保留新的字符串副本
  3. 然后还有一个怪癖,这是我变得相当可疑的地方,即-dealloc方法看起来像这样:

    - (void) dealloc {
        [[self wwanServiceId] release];
        [super dealloc];
    }
    
    那么那里会发生什么?它将release wwanServiceId afaik,正如我所说,只有触及记忆管理的时间(如果我没有错过任何东西,但我觉得非常确定)是自动释放并保留它。

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

    因为据我所知,如果这个setter被调用一次,它将放置一个autorelease(将来为-1),做一个retain(+1)并且当调用析构函数时它会做“最终发布”(-1)。

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

4 个答案:

答案 0 :(得分:2)

您写道:

  

这里wwanServiceIdis是该类的NSString成员,对我而言,这似乎是一种奇怪的方式。据我所知,它会首先在对象上放一个自动释放,基本上说:“每当这个对象似乎不被使用时,为我释放它我并不在乎”然后副本将保留计数+1在.... wwanServiceId?还是newValue?我想是第一个。

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

前一位员工写道:

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

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

表达式[newValue copy]表示:读取存储在newValue中的引用,用它来定位对象(称之为对象B),制作该对象的副本以生成新对象(称之为对象C),并返回对新对象的引用。这个新对象归copy的调用者所有,因此无需retain

最后,赋值将对象C的引用存储到wwanServiceId

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

  1. 答:wwanServiceId引用的原始对象,已自动释放,以删除wwanServiceId的所有权。
  2. B:newValue引用的对象,未触动
  3. C:通过wwanServiceId
  4. 拥有的新创建的B副本

    为什么代码中的“autorelease”和上面的“最多三个不同”?

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

    [self setWwanServiceId:[self wwanServiceId]]

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

    所以前一位员工所写的并不是“错误的”,但正如其他一些答案所暗示的那样,这可能是一种不同寻常的风格。你看到这篇文章的另一种方式是:

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

    还确保在复制后发生任何删除。

    HTH。

答案 1 :(得分: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建议直接使用initdealloc方法中的实例方法,而不是在那里使用getter和setter。

答案 2 :(得分: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中自动释放对象。您可以直接在两种方法中释放对象。

答案 3 :(得分:0)

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

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

我的意思是:没有必要将var的所有权授予自动释放池(它可能在应用程序结束时耗尽)。只需释放伊娃。如果有人使用它,没问题,它将有一个强烈的参考。否则它将被解除分配。