Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa strategy for pointer/memory management

I see a lot of code, particularly in Apple example code, that resembles the following:

   EditingViewController *controller = [[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil];
    self.editingViewController = controller;
    [controller release];

Is there any reason in particular that the approach above proves beneficial over:

self.editingViewController = [[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil];

Trying to understand if there is a strategy for the above.

Thanks!

like image 622
Coocoo4Cocoa Avatar asked Dec 17 '22 09:12

Coocoo4Cocoa


1 Answers

On first glance, it would seem that your example would work, but in fact it creates a memory leak.

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) EditingViewController *editingViewController;

So in your first example:

EditingViewController *controller = 
    [[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil];
// (1) new object created with retain count of 1

self.editingViewController = controller;
// (2) equivalent to [self setEditingViewController: controller];
// increments retain count to 2

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

But for your second example:

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

    // (1) new object created with retain count of 1
    [[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil];

// oops! retain count is now 2

By calling the autorelease method on your new object before passing it to the setter, you ask the autorelease pool to take ownership of the object and release it some time in the future, so for a while the object has two owners to match its retain count and everything is hunky dory.

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

    // (1) new object created with retain count of 1
    [[[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil]

        // (2) give ownership to autorelease pool 
        autorelease];

// okay, retain count is 2 with 2 owners (self and autorelease pool)

Another option would be to assign the new object directly to the instance variable instead of the property setter. Assuming your code named the underlying instance variable editingViewController:

// (2) assignment to an instance variable doesn't change retain count
editingViewController = 

    // (1) new object created with retain count of 1
    [[EditingViewController alloc] initWithNibName:@"EditingView" bundle:nil];

// yay! retain count is 1

That's a subtle but critical difference in the code. In these examples, self.editingViewController = x is syntactic sugar for [self setEditingViewController: x] but editingViewController is a plain old instance variable without any retain or copy code generated by the compiler.

See also Why does this create a memory leak (iPhone)?

like image 100
Don McCaughey Avatar answered Dec 24 '22 01:12

Don McCaughey