【问题标题】:Basic memory management confusion基本的内存管理混乱
【发布时间】:2012-08-30 08:00:22
【问题描述】:

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

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

例如

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

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

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

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

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

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

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

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

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

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

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

【问题讨论】:

    标签: objective-c memory-management


    【解决方案1】:

    你写道:

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

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

    前员工写道:

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

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

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

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

    所以最多涉及三个不同的对象:

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

    为什么是代码中的“autorelease”和上面的“maximum of three distinct”?

    可以使用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。

    【讨论】:

    • 非常有帮助,非常感谢您花时间真正解释如此深层次所涉及的概念。感谢您花时间解释为什么可以这样做,给了我很多见解,希望也能帮助我进一步了解我的前任所采用的风格。
    【解决方案2】:

    简短而有用:

    这是错误的:

    - (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。

    【讨论】:

    • 谢谢,这让我在看到那段代码时的所有顾虑都得到了启发。我感到很困惑,认为这可能是一种很好的做法,或者像以前那样去做,但是看到dealloc[[self wwanServiceId] release] 感到非常不安全,设置autorelease 感觉很奇怪,而且它给了我很多混乱。所以感谢您澄清这一点,我需要指导!
    • 酷,这就是 StackOverflow 的用途
    【解决方案3】:

    调试一下看看。

    [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 中自动释放对象。您可以直接在这两种方法中释放对象。

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-21
        • 2012-07-31
        • 1970-01-01
        • 2012-05-16
        • 1970-01-01
        相关资源
        最近更新 更多