views:

119

answers:

2

I'm getting reports of memory leaks in my app, but I can't track down exactly what is happening. I have a function which takes out an old view and swaps in a new view. I'm not using NavControllers or any @properties; I'm managing my top-level window directly.

-(void)swapInView:(UIViewController*)newViewController
{
    [currentViewer.view removeFromSuperview];
    printf("Old viewController (%p) has count of %d; now releasing\n",
        currentViewer, [currentViewer retainCount]);
    [currentViewer release];
    currentViewer = 0;

    currentViewer = newViewController;
    [currentViewer retain];

    [mainWindow addSubview:currentViewer.view];
    [mainWindow bringSubviewToFront:currentViewer.view];
}

When run the code, I show that the current view controller is being deallocated, and then my dealloc method for that view controller is getting called. But, Instruments/leaks still reports it as a leak. For instance, I get this print out:

Old viewController (0x119f80) has count of 1; now releasing
Deallocating WelcomeScreenViewController

I can verify from the address, that this is the same object being allocated previously.

My external code looks something like this:

MyViewController *theViewController = [[MyViewController alloc]
                                        initWithNibName:nil
                                                 bundle:nil];
[GameMaster swapInNewView:theViewController];
[theViewController release];

Does anybody have any suggestions of how to track down what is going on? I'm using the 3.1.2 SDK, but I was also seeing this on earlier SDK's.

A: 

Note the documentation for the removeFromSuperview method:

"Unlinks the receiver from its superview and its window, removes it from the responder chain, and invalidates its cursor rectangles. The receiver is also released"

I'm not sure if this is your problem or not, but doing:

[exampleViewController.view removeFromSuperview];
[exampleViewController release];

In my application causes a delayed crash due to over-releasing an object.

Mark Hammonds
True, but I am retaining the viewController myself when I'm setting it, and then releasing after I remove it, so I don't crash.
Nathan S.
+3  A: 

Huh. This was a fun one. I wrote a quick test to make sure neither of us was insane.

In the end, it all comes down to the calling code:

[[MyViewController alloc] initWithNibName:nil bundle:nil];

When you initialize a view controller, its view object has not yet been defined, and will not be defined until requested.

Since you specify nil for the nib name, you must override loadView in your UIViewController subclass in order to set the view object properly. See Apple's documentation for details.

The default implementation of loadView apparently does some behind-the-scenes magic, and that magic can result in a memory leak.

So: when you make this call:

[mainWindow addSubview:currentViewer.view];

You are actually making two calls:

One: currentViewer.view, which results in a call to [currentViewer loadView], and
Two: [mainWindow addSubview:...], which attempts to add the newly loaded view.

Leaks identifies this line because of the first call, and not the second one.

To verify, simply modify the loadView method in your custom UIViewController subclasses:

- (void)loadView
{
    [self setView:[[UIView new] autorelease]];
}

This prevents the call to the default loadView and now there are no more leaks.

Obviously, once you develop this application further, you will either have to put something more meaningful in loadView, or use nibs.

e.James
Fantastic! That seems to have fixed things. It ends up (I unknowingly copied this from Apple's sample code) that if you pass "nil" into initWithNibName, it will look for a nib file with the same name as the class and load it. But, apparently it leaks memory too...
Nathan S.