views:

2027

answers:

6

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?

//creates memory leak
self.editMyObject = [[MyObject alloc] init];

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

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

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).

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

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

  editMyObject = [obj retain];
}
Andrew Grant
except that if `editMyObject == obj`, this could fail horribly, because you'd be releasing the object (potentially deallocating it), and then attempting to retain a deallocated pointer.
Dave DeLong
+3  A: 

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.

Chuck
+6  A: 

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

@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].

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.

[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.

Barry Wark
I have an NSMutableArray. I've set it to copy but when I do this [self.List addObject:myobject], I get an uncaught exception. Setting it back to retain works fine. Any suggestions?
4thSpace
I believe that a @property(copy) will use -copy to create the copy, even if the new assignment is a mutable type. Thus you get an immutable copy (you would normally use -mutableCopy to create a mutable copy), causing the exception when you try to mutate the assigned value....
Barry Wark
[cont'd]You can use @property(retain) and assign like self.List = [[mutableArr mutableCopy] autorelease].
Barry Wark
+1  A: 

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:

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.

e.James
In a desktop app, I would usually do this. However, on the iPhone (or any memory constrained environment), using autorelease can cause memory usage to spike unnecessarily. Stick with retain/release unless you need autorelease.
Barry Wark
Hmm. I'm definitely coming from the desktop side on this. Can you explain what causes the spike? It seems to me that no extra memory is allocated in this case, but my iPhone coding experience is limited.
e.James
I don't know the details but it's well documented that autorelease is a fairly expensive call on the iPhone.
4thSpace
I did some searches for it, and I haven't turned up much. Everything I have read says: "autorelease is *slightly* more expensive than release, and that makes sense from a conceptual point of view. Does anyone have links to said documentation?
e.James
I opened a question to explore this issue: http://stackoverflow.com/questions/613583/why-is-autorelease-especially-dangerous-expensive-for-iphone-applications
e.James
+3  A: 

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.

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:

@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].

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).

See also http://stackoverflow.com/questions/574029/cocoa-strategy-for-pointer-memory-management

Don McCaughey
A: 

It was agreed and explained that the code below does not have a leak (assuming @property retain and @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.

rudifa