views:

273

answers:

2

In my iPhone app, I have a "statistics" view that is displayed by the following method - the method itself is called when the user touches a button. This method is in a UINavigationController

- (void) showStatsView {
    StatsViewController *statsViewController = [[StatsViewController alloc] initWithNibName:@"Stats" bundle:[NSBundle mainBundle]];
    [self pushViewController:statsViewController animated:YES];
    [statsViewController release]; //                       (1)
}

In the statistics view itself, an NSDictionary, "statsDict" is used. It's declared as a property with the following options: (nonatomic, retain).

In my StatsViewController, the viewDidLoad method creates the instance of the NSDictionary like so: MyAppDelegate *appDelegate= (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; stats = [[NSDictionary alloc] initWithContentsOfFile:[appDelegate statsFilePath]];

My dealloc method for StatsViewController looks like:

 - (void)dealloc {

    /* 
    dealloc'ing other stuff here which is irrelavent
    */
    [statsDict release]; //                 (2)
    [super dealloc];
}

My problem occurs if I attempt to reload the Statistics view after it's been shown once, that is - I press the button which causes showStatsView to fire, which loads the Statistics view (for the first time), and all is currently fine. If I then press a button on the Statistics View to return back to the main menu (which is done via a call to a method which uses [self popToViewController:MainMenuViewController]; ) - from here, if I press the button which loads the Statistics view again (for the second time), my app crashes with EXC_BAD_ACCESS.

If I remove the line marked (1) in showStatsView, then it works fine, as it does if I remove the line marked (2) in dealloc. However, from what I've read, I should release statsDict, as I allocated it, likewise, I should release statsViewController, as I allocated that too. However, if I do both - it crashes! What should I do?

Have I missed some step in my understanding of objective-c memory management?

+1  A: 

The reason your app doesn't crash when you remove (1) or (2) is that both of those have the effect of intentionally leaking the dictionary (at point 2 by leaking the dictionary, and at point 1 by leaking the dictionary's owner, so -dealloc never gets called.) Obviously that's not something you want to do. Your understanding of the contract is correct, but I think you are misunderstanding how your object is being initialized.

You didn't reproduce your -init code here, but at a guess, I think the issue here is that -viewDidLoad only gets called once when the view is unarchived. So on subsequent creations of the statsViewController, the dictionary isn't allocated.

If you move the line allocating and initting the NSDictionary from -viewDidLoad into the -init method of statsViewController, everything will work fine without your having to intentionally leak objects. -init is going to be called every time you create a new statsViewController. If you don't have an -init method, create one (and don't forget to call [super init]).

Best of luck.

peterb
Good point with `viewDidLoad` versus `init`. Keep in mind, though, that `viewDidLoad` can get called multiple times (views get unloaded if they're not visible and their controller gets a `didReceiveMemoryWarning` message, for example, and the view has to be loaded again later). As peterb said, initializing `statsDict` once for every instance of the view controller is best done in `init`.
Tim
AFAIK when the StatsViewController is unfrozen from the NIB with initWithNibName. There will be no init message sent to the object.
VoidPointer
Be that as it may, the StatsViewController _is being dealloced_. We know this because your -dealloc method is running. So you'll need to reinitialize that dictionary somehow when subsequent StatsViewControllers are created.
peterb
+2  A: 

Your understanding of the contract appears correct (assuming you mean "statsDict =" where you say "stats =").

So the problem is somewhere else, likely breaking the contract somewhere else.

I would suggest you turn on some memory debugging with environment variables NSZombieEnabled and NSAutoreleaseFreedObjectCheckEnabled and see if it tells you which objerct is being over released.

Peter N Lewis
+1 for NSZombieEnabled - super-helpful in finding calls to deallocated objects.
Tim