views:

1159

answers:

3

Xcode Instruments claims that the below code results in memory leak. As far as I understand, the following happens when assigning a property:
* The old value is autoreleased
* The new value is retained
* The new value is assigned obviously
With that it mind, how come I have a memory leak and how do I resolve it?

"TestProjectViewController.h":

#import <UIKit/UIKit.h>

@interface TestProjectViewController : UIViewController {
    NSMutableArray* array;
}

@property (nonatomic, retain) NSMutableArray* array;

@end

"TestProjectViewController.m":

#import "TestProjectViewController.h"

@implementation TestProjectViewController

@synthesize array;

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    for(int i = 0; i < 5; i++) {
        self.array = [[NSMutableArray alloc] init];
        [self.array addObject:@"Hello world #1"];
        [self.array addObject:@"Hello world #2"];
    }
}
+3  A: 
self.array = [[NSMutableArray alloc] init];

Here you retain new array object twice - when allocate it and then in setter method. One of the following should fix that:

self.array = [[[NSMutableArray alloc] init] autorelease];
// or
self.array = [NSMutableArray arrayWithCapacity:someNumber];

and also do not forget to release your array in dealloc method.

Vladimir
do I need to release the array manually when it is a property with attribute "retain"?
AO
Most definitely. You can also release the array by calling `self.array = nil;`
kubi
yes, property "retain" attribute defines behaviour of corresponding setter method - you increment objects retain count and so must release that object after that. If you change property value- old object will be released automatically. If you destroy your object you must release all (previously retained) its members. You can call `self.array = nil` in dealloc method instead of explicit release if you want
Vladimir
+1  A: 

Each time through the loop, you alloc a new array. Each array starts with a refcount of 1, and assigning to the property increases it to 2. You need to release these arrays so that the property is the only "owner" of the object.

For example, you could do this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    for(int i = 0; i < 5; i++) {
        self.array = [[NSMutableArray alloc] init];
        [self.array addObject:@"Hello world #1"];
        [self.array addObject:@"Hello world #2"];
        [self.array release];
    }
}

or do this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    for(int i = 0; i < 5; i++) {
        self.array = [[[NSMutableArray alloc] init] autorelease];
        [self.array addObject:@"Hello world #1"];
        [self.array addObject:@"Hello world #2"];
    }
}
Kristopher Johnson
A: 

This method leaks because you are already retaining the array (with alloc/init) before the property is. This results in each array being retained twice. You can either autorelease the array or release it at the end of the loop.

As a side note, why are you assigning a property like that in a loop? Only the last array will remain in that property. Instead, you could use a local variable and assign only the last array to the property:

- (void)applicationDidFinishLaunching:(UIApplication *)application { 
    NSMutableArray *tempArray = nil;

    for(int i = 0; i < 5; i++) {
        tempArray  = [[[NSMutableArray alloc] init] autorelease];
        [tempArray addObject:@"Hello world #1"];
        [tempArray addObject:@"Hello world #2"];
    }

    self.array = tempArray;
}
Colin Gislason