Thursday, April 23, 2009

Writing iPhone applications to handle Low Memory Situations

Part of writing a robust iPhone application is making sure it can recover from low memory situations. These can be not uncommon if you use memory hungry components such as the UIImagePickerController or UIWebView. When a low memory situation happens, the OS will try to free up memory from your application. If enough memory can’t be freed up, the OS will terminate your application.

Low Memory Events

In low memory situations, the OS will invoke the didReceiveMemoryWarning method on all of your view controller objects. You should override this method in your view controllers if there is an opportunity to free up any memory. I always override this method with at least a warning log message for testing purposes. If the view controller is not currently being displayed, the OS might release its view object.

Note: The documentation seems to suggest it will always do this, but in testing I’ve noticed this is not the case. It seems for less critical situations it will only call the didReceiveMemoryWarning method, and for more critical situations it will call didReceiveMemoryWarning and release the view object if not currently displayed.

Consequently, your view controllers must be designed such that they can recover in the case that its view object is released. This means that it must be written such that the view object can be unexpectedly released and then initialized again without leaking memory or putting the view in an unexpected or corrupted state.

When the view controller releases its view object, it calls the method setView:(UIView *)view with view set to nil. You can override this method to add code that will help to cleanly release the view and all the objects it contains.

Example

In the following example, the view contains an imageview object which is a member variable of the view controller. In order to release the imageview when the view gets released the setView method needs to be overridden to explicitly release the imageview (only for the condition of view == nil).

(Note that this example is for a non-NIB initialized view controller)
@interface MyViewController : UIViewController
{
    UIImageView *imageView;
}
@end

@implementation MyViewController

- (void)setView:(UIView *)aView
{
    if (aView == nil)
    {
        [imageView release];
    }    
    [super setView:aView];
}

- (void)loadView
{
    ...

    imageView = [[UIImageView alloc] initWithImage:myImage];

    ...
}

@end
Note that if the imageview was not a view controller member variable and was autoreleased into the view, then it would automatically be released when the view was released and this wouldn’t be necessary.

You can obviously choose not to release the objects in a view during low memory situations. If they are small objects, it’s probably safe to do so. However, the code for creating the view needs to check for the existence of previously created objects so they are not overwritten causing memory leaks and possibly putting the objects in corrupt or unexpected states:
- (void)loadView
{
    if (!imageView)
    {
        imageView = [[UIImageView alloc] initWithImage:myImage];
    }
}

Testing

Simulating low memory events should be an integral part of your testing. You can do this with the iPhone simulator by selecting the menu option: iPhone Simulator -> Hardware -> Simulate Memory Warning. If you did like me and put log statements inside the didReceiveMemoryWarning methods, you will see these messages in the console window. Also be sure to run the simulator with the Instruments tool in Leaks mode to check for memory leaks.

0 comments:

Followers

About Me