views:

108

answers:

2

I've written code to restore the state of my app, but there's a memory leak in the NSMutableArray. I'm new to Xcode so I apologize if this is something trivial I have overlooked. Any help is appreciated. lq

AppDelegate.m

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [rootViewController restoreState];
}

RootViewController.h

@interface rootViewController : UIViewController {
    NSMutableArray  *offendingNSMutableArray;
}
@property (nonatomic, retain) NSMutableArray *offendingNSMutableArray;

RootViewController.m

@synthesize offendingNSMutableArray;

- (void)restoreState {
    // Gets an array stored in the user defaults plist
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    self.offendingNSMutableArray = [[NSMutableArray alloc]    
            initWithArray:[userDefaults objectForKey:kArrayValue]];
}

- (void)viewDidUnload {
    self.offendingNSMutableArray = nil;
}

- (void)dealloc {
    [offendingNSMutableArray release];
}
A: 

If you set it to nil in viewDidUnload, what are you going to release in dealloc? You should just do

self.offendingNSMutableArray = nil;

in dealloc, that's the common way for retained properties.

EDIT: See it now from the comment above. You need autorelease where you do alloc/init. Property setter will do retain.

unbeli
Using Instruments I'm showing Leaked Object: Malloc 32 Bytes on the alloc init line as well as Leaked Object: NSCFArray on the alloc init line.
Lauren Quantrell
I am told (by eminent Obj-C folk around here) that calling property setters in `dealloc` is a bad idea because it may lead to notifications firing, which you don't want in mid teardown. As for the memory leak -- there certainly is one, see @beefon's comment.
walkytalky
@walkytalky this is what is recommended here http://developer.apple.com/mac/library/documentation/cocoa/conceptual/objectivec/articles/ocProperties.html. Yes, I see the leak now, my bad
unbeli
The section on dealloc in that very document says: "Note: Typically in a dealloc method you should release object instance variables directly (rather than invoking a set accessor and passing nil as the parameter)". There's an exception for the synthetic ivar case, where direct release is not an option, but otherwise that seems pretty unambiguous.
walkytalky
well, go ahead and read the next sentence. Of course everyone is using modern runtime and synthesizing, what else?
unbeli
A: 

OK, so it looks like I solve the leak by adding autorelease:

- (void)restoreState {
    // Gets an array stored in the user defaults plist
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    self.offendingNSMutableArray = [[[NSMutableArray alloc]    
        initWithArray:[userDefaults objectForKey:kArrayValue]] autorelease];
}

But I thought I didn't want to use autorelease because I reference offendingNSMutableArray in other places in the app?

Lauren Quantrell
The setter will `retain` the object for itself. This `autorelease` relinquishes the local ownership you got via `alloc`. Putting it all in one line like that makes the semantics a bit opaque -- it'd be clearer to `alloc`/`init` in one line, assign in a second and then explicitly `release` in a third -- but the end result is (nearly) the same.
walkytalky