Images and Caching

If you are working with a large number of images in an iPhone application, read on, chances are I can save you some trouble when it comes to memory management. Let me provide some background information…

I’ve been working on an application which has images divided into several sub-directories:

/imageSet01/thumbnails/pic01.png
/imageSet01/thumbnails/pic02.png

/imageSet10/thumbnails/pic01.png
/imageSet10/thumbnails/pic02.png

In addition, for each set of thumnails there is also an image that is viewable fullscreen, this image can also be saved to the camera roll if the user opts to do so.

/imageSet10/wallpaper/pic01.png
/imageSet10/wallpaper/pic02.png

/imageSet10/wallpaper/pic01.png
/imageSet10/wallpaper/pic02.png

All told, there are more 12 images sets, with up to 70 images per set. In addition, I’ve integrated a coverflow viewer that allows users to view a series of thumnails images by swiping left/right. To give you an idea of the space allocated for the coverflow, each image has a texture allocated that consumes 256K. With 70 images, I’m pushing 17MB. That does not include allocating each UIImage resource (from a file) to be shown on the texture.

With all that going for me, when it came to loading images for backgrounds, creating views, data structures, etc, you can see why it was important to keep a tight reign on allocation/releasing of objects.

One concept that wasn’t apparent up front, and the primary reason for this post, is that your choice of how you allocate UIImage can make a significant impact on your memory usage. What follows is a typical line of code, found in many examples and applications, for allocating images:

UIImageView *wallpaper = [[UIImageView alloc] 
  initWithImage:[UIImage imageNamed:@"/imageSet02/wallpaper/pic02.png"]];

The good news is, the iPhone will cache this image, so if you make this same request again, the image will be loaded from an internal cache versus loading from a resource. Bad news is, the iPhone will cache this image. In my application, this subtle caching of images for the coverflow as well as the fullscreen image preview, led to a significant problem, ultimately causing the application to crash.

Here’s another means to allocate an image and avoid the system caching:

UIImageView *wallpaper = [[UIImageView alloc]
  initWithImage:[UIImage imageWithContentsOfFile:@"/imageSet02/wallpaper/pic02.png"]];

So, with that long intro it really comes down to choosing between imageWithContentsOfFile and imageNamed, depending on your whether or not you prefer to have images cached. Now mind you, all of this is documented in the API, which is the segue to the second reason for this post – read the documentation closely! Finally, if you find a code example that looks to fit the bill for your application, again, consult the docs before simply copying/pasting.

  1. John,

    You’ve joined a bandwagon complaining about imageNamed. Do you actually have some insight into why the performance is so bad? Does the imageNamed cache ignore memory warnings?

    I’ve created a SO question for any answers.

    r

  2. Thank you, im working with more than 100 1700×1200 jpg’s in my app and had memory issues.
    This seems to fix the problem.

    Thanks very much!!!

  3. Do you have to release the image using the second method like this?

    NSString* imagePath = [[NSBundle mainBundle] pathForResource:@”inbox” ofType:@”png”];
    UIImage *inbox = [UIImage imageWithContentsOfFile:imagePath];
    UIImageView *inboxView = [[UIImageView alloc] initWithImage:inbox];

    //Do somthing

    [inbox release];
    [inboxView release];

    OR

    NSString* imagePath = [[NSBundle mainBundle] pathForResource:@”inbox” ofType:@”png”];
    UIImage *inbox = [[UIImage alloc] imageWithContentsOfFile:imagePath];
    UIImageView *inboxView = [[UIImageView alloc] initWithImage:inbox];

    //Do somthing

    [inbox release];
    [inboxView release];

  4. HI, I’m having the same problem with UIImage’s not releasing. I’m generating UIImages with a bit-bucket, creating them on the fly and swapping the UIImageView’s image. Is there a way to edit the UIImageView’s Image directly? (ie. change the color of a specific pixel, without removing the UIImage from the UIImageView, and get it to redraw.) Currently, I’m flushing the UIImage, making a new one, and assigning it to the UIImageView. This works. Shows no MemLeaks. But on the iPhone (3Gs) after about 100 image replacements, CRASHES. Cache’n issue? The memory summation seems to be hitting the phone’s limit if cache not releasing, however, Simulator does not show memory consumption with each image swap. Stays flatlined without leaks.

    Note: topologyImage array is the RGBA pixel-bucket. The REF variables are not released. Every attempt to do so, crashes next call. Without, Instruments reports no leaks.

    Make room for me on the imageNamed:bandwagon. However, imageWithCGImage seems to have the same cache’n nightmare. Any alternatives?

    =========

    CGColorSpaceRef colorSpaceRef=CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast;
    CGColorRenderingIntent renderingIntent=kCGRenderingIntentDefault;
    CGDataProviderRef provider=CGDataProviderCreateWithData(NULL,topologyImage,(I*I*4),NULL);
    CGImageRef imageRef=CGImageCreate(I,I,8,4*8,4*I,colorSpaceRef,bitmapInfo,provider,NULL,false,renderingIntent);

    UIImage *img=[UIImage imageWithCGImage:imageRef];
    if( IMG[NDXtopo].vw ) {
    [IMG[NDXtopo].vw setImage:img];
    }
    else {
    IMG[NDXtopo].vw=[[UIImageView alloc] initWithImage:img];
    [master.view addSubview:IMG[NDXtopo].vw];
    }

Comments are closed.