【问题标题】:Why does this create a memory leak (iPhone)?为什么这会造成内存泄漏(iPhone)?
【发布时间】:2010-10-11 09:50:36
【问题描述】:
//creates memory leak
  self.editMyObject = [[MyObject alloc] init];

//does not create memory leak
  MyObject *temp = [[MyObject alloc] init];
  self.editMyObject = temp;
  [temp release];

第一行代码会造成内存泄漏,即使您在类的 dealloc 方法中执行 [self.editMyObject release] 也是如此。 self.editMyObject 是 MyObject 类型。第二行不会导致内存泄漏。是第一行不正确还是有办法释放内存?

【问题讨论】:

  • 三个好答案。请注意确定谁也可以给出答案。

标签: objective-c iphone xcode memory-management


【解决方案1】:

正确的行为取决于editMyObject @property 的声明。假设它被定义为

@property (retain) id editMyObject; //id may be replaced by a more specific type

@property (copy) id editMyObject;

然后通过self.editMyObject = 分配保留或复制分配的对象。由于[[MyObject alloc] init] 返回一个保留对象,你作为调用者拥有,你有一个额外的 MyObject 实例保留,因此它会泄漏,除非它有一个匹配的释放(如在第二个块中)。我建议你阅读Memory Management Programming Guide[2]。

你的第二个代码块是正确的,假设属性是如上所述声明的。

附言您不应在 -dealloc 方法中使用 [self.editMyObject release]。您应该调用[editMyObject release](假设支持@property 的ivar 称为editMyObject)。调用访问器(通过self.editMyObject 对@synthesized 访问器来说是安全的,但是如果被覆盖的访问器依赖于对象状态(这可能在-dealloc 的调用位置无效或导致其他副作用,那么您有一个错误:调用访问器。

[2] Cocoa 中的对象所有权规则非常简单:如果你调用一个方法,其签名中有alloc,或者copy(或者使用+[NSObject new],基本上相当于[[NSObject alloc] init]),那么您“拥有”返回的对象,并且您必须通过release 平衡您获得的所有权。在所有其他情况下,您不拥有从方法返回的对象。如果您想保留它,您必须使用retain 获得所有权,然后使用release 释放所有权。

【讨论】:

  • 我有一个 NSMutableArray。我已将其设置为复制,但是当我这样做 [self.List addObject:myobject] 时,我得到了一个未捕获的异常。将其重新设置为保留工作正常。有什么建议吗?
  • 我相信 @property(copy) 将使用 -copy 来创建副本,即使新分配是可变类型。因此你得到一个不可变的副本(你通常会使用 -mutableCopy 来创建一个可变的副本),当你尝试改变分配的值时会导致异常......
  • [cont'd] 你可以使用 @property(retain) 并像 self.List = [[mutableArr mutableCopy] autorelease] 一样赋值。
【解决方案2】:

同意并说明下面的代码没有泄露 (假设 @property 保留和 @synthesize 为 editMyObject):

//does not create memory leak
MyObject *temp = [[MyObject alloc] init];
self.editMyObject = tempt;
[temp release];

问题:以下不使用临时指针的代码有什么问题吗?

//does not create memory leak ?
self.editMyObject = [[MyObject alloc] init];
[editMyObject release];

对我来说这看起来不错。

【讨论】:

  • 您需要确保在您的 dealloc 方法中该对象也被释放:[editMyObject release] 或 self.editMyObject = nil;
【解决方案3】:

按照 Cocoa 和 Cocoa-touch 中的约定,使用 [[SomeClass alloc] initX][SomeClass newX] 创建的任何对象的保留计数为 1。完成新实例后,您有责任调用 [someClassInstance release],通常是在您的 dealloc 方法中。

当您将新对象分配给属性而不是实例变量时,这会变得棘手。大多数属性被定义为retaincopy,这意味着它们要么在设置时增加对象的保留计数,要么复制对象,而原始对象保持不变。

在您的示例中,您的 .h 文件中可能有这个:

@property (retain) MyObject *editMyObject;

所以在你的第一个例子中:

// (2) property setter increments retain count to 2
self.editMyObject = 

    // (1) new object created with retain count of 1
    [[MyObject alloc] init];

// oops! retain count is now 2

当您使用 alloc/init 创建 MyObject 的新实例时,它的保留计数为 1。当您将新实例分配给self.editMyObject 时,实际上是在调用@synthesize editMyObject 时编译器为您创建的-setEditMyObject: 方法。当编译器看到self.editMyObject = x 时,它会将其替换为[self setEditMyObject: x]

在你的第二个例子中:

MyObject *temp = [[MyObject alloc] init];
// (1) new object created with retain count of 1

self.editMyObject = temp;
// (2) equivalent to [self setEditMyObject: temp];
// increments retain count to 2

[temp release];
// (3) decrements retain count to 1

您持有新对象足够长的时间以释放它,因此保留计数是平衡的(假设您在 dealloc 方法中释放它)。

另见Cocoa strategy for pointer/memory management

【讨论】:

    【解决方案4】:

    第一个版本创建了一个没有匹配版本的对象。当您分配对象时,这意味着您是该对象的所有者。您的 setter 可能会保留该对象(应该如此),这意味着您现在拥有该对象两次。您需要释放来平衡对象的创建。

    如果你打算使用 Cocoa,你应该阅读the Cocoa memory management guide。学了不难,但是一定要学,不然会遇到很多这样的问题。

    【讨论】:

      【解决方案5】:

      其他人已经解释了它导致内存泄漏的原因,所以我将插话如何避免“temp”变量并仍然防止内存泄漏:

      self.editMyObject = [[[MyObject alloc] init] autorelease];
      

      这将使您的(保留)财产成为新对象的唯一所有者。结果与第二个示例完全相同,但没有临时对象。

      【讨论】:

      • 在桌面应用程序中,我通常会这样做。但是,在 iPhone(或任何内存受限的环境)上,使用自动释放可能会导致内存使用量不必要地飙升。除非您需要自动释放,否则请坚持使用保留/释放。
      • 嗯。我肯定是从桌面方面来的。你能解释一下导致峰值的原因吗?在我看来,在这种情况下没有分配额外的内存,但我的 iPhone 编码经验是有限的。
      • 我不知道细节,但有据可查的是,自动释放在 iPhone 上是一个相当昂贵的调用。
      • 我对它进行了一些搜索,但没有出现太多。我读过的所有内容都说:“autorelease 比 release 稍微贵,从概念的角度来看这是有道理的。有人有上述文档的链接吗?
      • 我打开了一个问题来探讨这个问题:stackoverflow.com/questions/613583/…
      【解决方案6】:

      您的属性被声明为“保留”,这意味着传入的对象会自动保留。

      因为你的对象已经有一个来自 alloc/init 的引用计数,所以有两个引用,我假设只有一个版本(在你的析构函数中)。

      基本上对 self.editMyObject 的调用确实是这样做的;

      -(void) setEditMyObject:(MyObject*)obj
      {
        if (editMyObject)
        {
          [editMyObject release];
          editMyObject = nil;
        }
      
        editMyObject = [obj retain];
      }
      

      【讨论】:

      • 除非是editMyObject == obj,这可能会严重失败,因为您将释放对象(可能会释放它),然后尝试保留释放的指针。
      猜你喜欢
      • 2017-07-01
      • 1970-01-01
      • 2019-09-08
      • 2011-06-17
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多