views:

1933

answers:

4

Similar to the known memory leak issue regarding creating and destroying an instance of the UIImagePickerController, I'm finding similar problems regarding instances of the UIViewController class. The recommended way to use the UIImagePickerController is to create the instance once and keep it around for the lifetime of the application, eventhough this will use up memory that you may need elsewhere.

The situation I'm dealing with involves 2 instances of the UIViewController class. On start up, the first instance is created and its view is added to another "main" UIViewController class that is part of the MainWindow.xib. On this first instance is an "info" button that when tapped, switches to a new instance of the UIViewController class (if it hasn't already been created). The "main" UIViewController manages this switching with the usual flip animation. The basic setup can be seen in the "Beginning iPhone Development: Exploring the iPhone SDK" book by Dave Mark.

The problem that arises is that once the "info" button is tapped the very first time, memory is allocated to the new second UIViewController instance and it is not released until the application ends. Due to the number of elements on this info view, it uses approximately 1MB of memory once instantiated and its view is added to the superview. Any attempts to consistently destroy and recreate this instance results in a memory leak, similar to the one that exists if you try to do the same thing to instances of the UIImagePickerController class. I suspect that the root cause is the same between the two classes.

The crux of my problem involves needing to have as much memory freed before I allow the user to take a picture with the camera. However, once the user has taken a picture and they see the resulting image the first time through, they are allowed to tap on the "info" button that exists on the first UIViewController instance. Once tapped, the "main" UIViewController removes the existing UIViewController's view and replaces it with the one for the info screen. The info screen has a "back" button to switch the views back. However, once the user leaves the info screen and chooses to take another picture with the camera, the memory allocated to the info screen is still in memory.

The UIImagePickerController class temporarily uses almost 15-18MB while it processes the 2 megapixel image before releasing its internal references and the "imagePickerController:didFinishPickingImage" delegate is called. I am running into low memory alerts once the second UIViewController instance has been created via the info button and then the user chooses to take another picture.

Memory technically is not leaking whether you take pictures over and over with or without tapping on the info button in my case, but due to other issues regarding background processes on the iPhone (Safari, etc.) that are beyond your control, you MUST free up as much memory as possible while working with things like the camera.

Any advice on how to cleanly create and destroy instances of the UIViewController class such that memory does not leak?

+1  A: 

One way of reducing your memory usage would be to resize the image to whatever size you want (unless of course you want a 320x480 image). That helped a lot in my case.

Does the second viewcontroller you are talking about change? If not, then it would be best to make it a singleton and use the same instance. You can any time change the values used by the viewcontroller. This article explains how you can create singleton objects (with the code)

Another article here shows the use of a singleton class (though different from your use case, it will clarify how to use singletons)

I would suggest creating a singleton object for UIImagePickerController too.

lostInTransit
+1  A: 

Do you have any cycles in your chain of ownership? Something like:

@interface FirstViewController: UIViewController {
  SecondViewController *secondViewController;
}
@end

@interface SecondViewController: UIViewController {
  FirstViewController *firstViewController;
}
@end

If you don't explicitly break this cycle when you discard these view controllers, they'll leak.

Also, I believe you're responsible for releasing all top level objects that are loaded from a nib file when you no longer need them.

Don McCaughey
how do you explicitly break the cycle?
nico
By having the FirstViewController instance explicitly do [secondViewController release]; secondViewController = nil; (or the reverse) at the point in your app when you know you're going to discard them.
Don McCaughey
could I put those 2 lines of code inside both of the Controllers dealloc methods? would that do the job?
nico
No, that's the problem -- dealloc will never get called. You put them in a method that's called by some higher level object (like a tab bar controller or app delegate) before it discards your paired controllers
Don McCaughey
+4  A: 

Are you loading the second view controller from a NIB? If so, you'll want to check that you're releasing the associated memory correctly.

Here's what a typical NIB-based view controller looks like in my projects.

SomeViewController.h

@interface SomeViewController : UIViewController {
    UILabel *someLabel;
}

@property (nonatomic, retain) IBOutlet UILabel *someLabel;

@end

SomeViewController.m

@implementation SomeViewController

@synthesize someLabel;

- (void)dealloc {
    // Release our retained IBOutlets
    self.someLabel = nil;
    [super dealloc];
}

@end
PCheese
why do you do self.someLabel = nil instead of [someLabel release], at the dealloc method? is it the same?
nico
It's not exactly the same. `self.someLabel = nil` will send release to the object, and then assigns nil to the property. Sending release to the object yourself does not set the property to nil, you have to do it all by yourself. I always use `[_iVar release], _iVar = nil;` which does it all, because I don't declare avery instance variable as a property.
JoostK
A: 

Perhaps you could save the image out before transitioning into the info view. After saving, release the image and then transition into the info view. If the user goes back, load the image from the folder.

CodeSpyder