为什么这会造成内存泄漏(iPhone)?

时间:2009-03-04 23:36:17

标签: objective-c iphone xcode memory-management

//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。第二行不会导致内存泄漏。第一行是不正确还是有办法释放内存?

6 个答案:

答案 0 :(得分:10)

正确的行为取决于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]。

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

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

[2] Cocoa中的对象所有权规则非常简单:如果您在其签名中调用alloccopy的方法(或使用基本相当于+[NSObject new]的{​​{1}} [[NSObject alloc] init]),然后您“拥有”返回的对象,您必须平衡所有权的获得与release。在所有其他情况下,您不拥有从方法返回的对象。如果您想保留它,则必须使用retain获得所有权,然后使用release发布所有权。

答案 1 :(得分:8)

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

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

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

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

  editMyObject = [obj retain];
}

答案 2 :(得分:4)

按照惯例,在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

使用MyObject / alloc创建init的新实例时,其保留计数为1。当您将新实例分配给self.editMyObject时,实际上是在-setEditMyObject:时调用编译器为您创建的@synthesize editMyObject方法。当编译器看到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

答案 3 :(得分:3)

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

如果您计划使用Cocoa,则应阅读the Cocoa memory management guide。一旦你学会了它并不难,但这是你必须学习的东西,或者你会遇到很多这样的问题。

答案 4 :(得分:1)

其他人已经涵盖了导致内存泄漏的原因,所以我只想说明如何避免'temp'变量并仍然防止内存泄漏:

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

这会使您的(retain)属性成为新对象的唯一所有者。与第二个示例完全相同的结果,但没有临时对象。

答案 5 :(得分:0)

同意并解释说下面的代码没有泄漏 (假设@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];

对我来说这看起来不错。