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

时间:2021-06-06 08:47:12
//creates memory leak
  self.editMyObject = [[MyObject alloc] init];

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

The first line of code creates a memory leak, even if you do [self.editMyObject release] in the class's dealloc method. self.editMyObject is of type MyObject. The second line incurs no memory leak. Is the first line just incorrect or is there a way to free the memory?

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

6 个解决方案

#1


10  

The correct behavior depends on the declaration of the editMyObject @property. Assuming it is delcared as

正确的行为取决于editMyObject @property的声明。假设它是delcared

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

or

@property (copy) id editMyObject;

then assignment via self.editMyObject = retains or copies the assigned object. Since [[MyObject alloc] init] returns a retained object, that you as the caller own, you have an extra retain of the MyObject instance and it will therefore leak unless it there is a matching release (as in the second block). I would suggest you read the Memory Management Programming Guide[2].

然后通过self.editMyObject =赋值保留或复制指定的对象。由于[[MyObject alloc] init]返回一个保留对象,你作为调用者拥有,你有一个额外的MyObject实例保留,因此它将泄漏,除非它有匹配的版本(如第二个块)。我建议你阅读内存管理编程指南[2]。

Your second code block is correct, assuming the property is declared as described above.

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

p.s. You should not use [self.editMyObject release] in a -dealloc method. You should call [editMyObject release] (assuming the ivar backing the @property is called editMyObject). Calling the accessor (via self.editMyObject is safe for @synthesized accessors, but if an overriden accessor relies on object state (which may not be valid at the calling location in -dealloc or causes other side-effects, you have a bug by calling the accessor.

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

[2] Object ownership rules in Cocoa are very simple: if you call a method that has alloc, or copy in its signature (or use +[NSObject new] which is basically equivalent to [[NSObject alloc] init]), then you "own" the object that is returned and you must balance your acquisition of ownership with a release. In all other cases, you do not own the object returned from a method. If you want to keep it, you must take ownership with a retain, and later release ownership with a release.

[2] Cocoa中的对象所有权规则非常简单:如果你调用一个具有alloc的方法,或者在其签名中复制(或者使用+ [NSObject new],这基本等同于[[NSObject alloc] init]),那么你“拥有”返回的对象,您必须平衡所有权的获得与发布。在所有其他情况下,您不拥有从方法返回的对象。如果您想保留它,您必须拥有保留所有权,然后通过版本获得所有权。

#2


8  

Your property is declared "retain" meaning that it the passed in object is automatically retained.

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

Because your object already had a reference count of one from alloc/init there's then two references and I'm assuming only one release (in your destructor).

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

Basically the call to self.editMyObject is really doing this;

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

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

  editMyObject = [obj retain];
}

#3


4  

By convention in Cocoa and Cocoa-touch, any object created using [[SomeClass alloc] initX] or [SomeClass newX] is created with a retain count of one. You are responsible for calling [someClassInstance release] when you're done with your new instance, typically in your dealloc method.

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

Where this gets tricky is when you assign your new object to a property instead of an instance variable. Most properties are defined as retain or copy, which means they either increment the object's retain count when set, or make a copy of the object, leaving the original untouched.

当你将新对象分配给属性而不是实例变量时,这很棘手。大多数属性被定义为保留或复制,这意味着它们可以在设置时增加对象的保留计数,或者创建对象的副本,保持原始状态不变。

In your example, you probably have this in your .h file:

在您的示例中,您可能在.h文件中包含此内容:

@property (retain) MyObject *editMyObject;

So in your first example:

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

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

When you create your new instance of MyObject using alloc/init, it has a retain count of one. When you assign the new instance to self.editMyObject, you're actually calling the -setEditMyObject: method that the compiler creates for you when you @synthesize editMyObject. When the compiler sees self.editMyObject = x, it replaces it with [self setEditMyObject: x].

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

In your second example:

在你的第二个例子中:

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

you hold on to your new object long enough to release it, so the retain count is balanced (assuming you release it in your dealloc method).

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

See also Cocoa strategy for pointer/memory management

另请参阅指针/内存管理的Cocoa策略

#4


3  

The first version creates an object without a matching release. When you alloc the object, it means you are an owner of that object. Your setter presumably retains the object (as it should), meaning you now own the object twice. You need the release to balance the object creation.

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

You should read the Cocoa memory management guide if you plan to use Cocoa at all. It's not hard once you learn it, but it is something you have to learn or you'll have a lot of problems like this.

如果您计划使用Cocoa,则应阅读Cocoa内存管理指南。一旦你学会了它并不难,但这是你必须学习的东西,否则你会遇到很多这样的问题。

#5


1  

Everyone else has already covered why it causes a memory leak, so I'll just chime in with how to avoid the 'temp' variable and still prevent a memory leak:

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

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

This will leave your (retain) property as the sole owner of the new object. Exactly the same result as your second example, but without the temporary object.

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

#6


0  

It was agreed and explained that the code below does not have a leak (assuming @property retain and @synthesize for editMyObject) :

同意并解释说下面的代码没有泄漏(假设@property保留和@synthesize for editMyObject):

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

Question : is anything wrong with the following code that does not use a temp pointer ?

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

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

To me this looks ok.

对我来说这看起来不错。

#1


10  

The correct behavior depends on the declaration of the editMyObject @property. Assuming it is delcared as

正确的行为取决于editMyObject @property的声明。假设它是delcared

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

or

@property (copy) id editMyObject;

then assignment via self.editMyObject = retains or copies the assigned object. Since [[MyObject alloc] init] returns a retained object, that you as the caller own, you have an extra retain of the MyObject instance and it will therefore leak unless it there is a matching release (as in the second block). I would suggest you read the Memory Management Programming Guide[2].

然后通过self.editMyObject =赋值保留或复制指定的对象。由于[[MyObject alloc] init]返回一个保留对象,你作为调用者拥有,你有一个额外的MyObject实例保留,因此它将泄漏,除非它有匹配的版本(如第二个块)。我建议你阅读内存管理编程指南[2]。

Your second code block is correct, assuming the property is declared as described above.

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

p.s. You should not use [self.editMyObject release] in a -dealloc method. You should call [editMyObject release] (assuming the ivar backing the @property is called editMyObject). Calling the accessor (via self.editMyObject is safe for @synthesized accessors, but if an overriden accessor relies on object state (which may not be valid at the calling location in -dealloc or causes other side-effects, you have a bug by calling the accessor.

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

[2] Object ownership rules in Cocoa are very simple: if you call a method that has alloc, or copy in its signature (or use +[NSObject new] which is basically equivalent to [[NSObject alloc] init]), then you "own" the object that is returned and you must balance your acquisition of ownership with a release. In all other cases, you do not own the object returned from a method. If you want to keep it, you must take ownership with a retain, and later release ownership with a release.

[2] Cocoa中的对象所有权规则非常简单:如果你调用一个具有alloc的方法,或者在其签名中复制(或者使用+ [NSObject new],这基本等同于[[NSObject alloc] init]),那么你“拥有”返回的对象,您必须平衡所有权的获得与发布。在所有其他情况下,您不拥有从方法返回的对象。如果您想保留它,您必须拥有保留所有权,然后通过版本获得所有权。

#2


8  

Your property is declared "retain" meaning that it the passed in object is automatically retained.

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

Because your object already had a reference count of one from alloc/init there's then two references and I'm assuming only one release (in your destructor).

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

Basically the call to self.editMyObject is really doing this;

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

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

  editMyObject = [obj retain];
}

#3


4  

By convention in Cocoa and Cocoa-touch, any object created using [[SomeClass alloc] initX] or [SomeClass newX] is created with a retain count of one. You are responsible for calling [someClassInstance release] when you're done with your new instance, typically in your dealloc method.

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

Where this gets tricky is when you assign your new object to a property instead of an instance variable. Most properties are defined as retain or copy, which means they either increment the object's retain count when set, or make a copy of the object, leaving the original untouched.

当你将新对象分配给属性而不是实例变量时,这很棘手。大多数属性被定义为保留或复制,这意味着它们可以在设置时增加对象的保留计数,或者创建对象的副本,保持原始状态不变。

In your example, you probably have this in your .h file:

在您的示例中,您可能在.h文件中包含此内容:

@property (retain) MyObject *editMyObject;

So in your first example:

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

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

When you create your new instance of MyObject using alloc/init, it has a retain count of one. When you assign the new instance to self.editMyObject, you're actually calling the -setEditMyObject: method that the compiler creates for you when you @synthesize editMyObject. When the compiler sees self.editMyObject = x, it replaces it with [self setEditMyObject: x].

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

In your second example:

在你的第二个例子中:

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

you hold on to your new object long enough to release it, so the retain count is balanced (assuming you release it in your dealloc method).

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

See also Cocoa strategy for pointer/memory management

另请参阅指针/内存管理的Cocoa策略

#4


3  

The first version creates an object without a matching release. When you alloc the object, it means you are an owner of that object. Your setter presumably retains the object (as it should), meaning you now own the object twice. You need the release to balance the object creation.

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

You should read the Cocoa memory management guide if you plan to use Cocoa at all. It's not hard once you learn it, but it is something you have to learn or you'll have a lot of problems like this.

如果您计划使用Cocoa,则应阅读Cocoa内存管理指南。一旦你学会了它并不难,但这是你必须学习的东西,否则你会遇到很多这样的问题。

#5


1  

Everyone else has already covered why it causes a memory leak, so I'll just chime in with how to avoid the 'temp' variable and still prevent a memory leak:

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

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

This will leave your (retain) property as the sole owner of the new object. Exactly the same result as your second example, but without the temporary object.

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

#6


0  

It was agreed and explained that the code below does not have a leak (assuming @property retain and @synthesize for editMyObject) :

同意并解释说下面的代码没有泄漏(假设@property保留和@synthesize for editMyObject):

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

Question : is anything wrong with the following code that does not use a temp pointer ?

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

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

To me this looks ok.

对我来说这看起来不错。