0

I'm using a standard caching method to cache some UIImages loaded from the Documents directory. The images are being displayed in a UITableView. They're pretty large – the images themselves are up to 600x600 and are displayed in imageviews that are 240x180 (on a retina display, so the res discrepancy is not large).

Loading the images in realtime causes some lag when a new cell is about to come onscreen. So I've implemented a caching method in the object that handles the image:

- (UIImage *)imageWithStyle:(NSString *)styleName {
    NSLog(@"looking for image %@", self.imageFileName);

    /* if we have a cached image in dictionary, return it */
    if (imageCache == nil) imageCache = [[NSMutableDictionary alloc] init];
    UIImage *returnImage = [imageCache objectForKey:styleName];
    if (returnImage != nil) {
        NSLog(@"returning cached image");
        return returnImage;
    }

    /* otherwise, look for image at path */
    NSString *path = [self cacheFilePathWithStyle:styleName];
    UIImage * originalImage = [[UIImage alloc] initWithContentsOfFile:path];

    /* if image doesnt exist at path, start download and return nil */
    if (originalImage == nil) {
        NSLog(@"image not found. downloading.");
        [self downloadImageFromS3];
        return nil;
    }

    /* found image at path */

    /* scale image for screen */
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2){
        returnImage = [UIImage imageWithCGImage:[[originalImage autorelease] CGImage] scale:2.0 orientation:UIImageOrientationUp];
        NSLog(@"scaling image for retina");
    } else {
        returnImage = [originalImage autorelease];
        NSLog(@"image scaled for standard resolution");
    }

    /* cache image in dictionary */
    NSLog(@"caching image");
    [imageCache setObject:returnImage forKey:styleName];

    return returnImage;
}

Before the tableview appears on screen, I force all of the image handling objects to run this caching method to ensure that the images are present in the dictionary to be retrieved when they need to be displayed. By the NSLog's I can see that things are working as they should.

I'm getting lag-free performance now, but only after the image has appeared once on screen. So, when I initially see the tableview, I scroll down and the NSLogs tell me that the images are being retrieved from the cache, but still I get the same loading lag. After a cell has appeared on screen once, it thereafter loads up with no lag.

Is there something I'm missing here? Is there something more that I have to to to actually cache the image? Loading it and putting it in the dictionary doesn't seem to do the trick.

Thanks!

UPDATE

I've given up on this for now. There are some attempts out there to force the images to load by drawing them in a new context, but I'm not familiar with core graphics programming at this point. I've tried some code that folks have shared, but with no luck.

Instead, I'm going to display low-res versions of the images while the tableview is scrolling and load high-res versions when the tableview stops scrolling as announced through its delegate methods. At least I know this approach will work with any number of images.

WrightsCS
  • 50,551
  • 22
  • 134
  • 186
CharlieMezak
  • 5,999
  • 1
  • 38
  • 54

2 Answers2

0

From the documentation for -[UIImage initWithContentsOfFile:]:

This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path.

My guess is that, by loading all images into memory, your app consumes too much memory, causing the UIImage class to release the image data it can reload from files later.

Ole Begemann
  • 135,006
  • 31
  • 278
  • 256
  • Thanks for the reply! Hmm, that seems like a good guess, but once I have scrolled through the entire list view (about 30 images), I no longer have the loading lag. So, it seems that the image ARE all cached at once at that point without any problem. I'll try caching just the first 10 or so images at first to see if it makes a difference. – CharlieMezak Jan 29 '11 at 15:54
  • No difference if I cache only a few images. I also tried using imageWithContentOfFile, which doesn't cache the image (a good idea anyway since I am caching the images myself). No luck. – CharlieMezak Jan 29 '11 at 16:07
  • Maybe `UIImage` loads the images lazily (i.e., it only loads them when they get drawn on screen). The documentation says something else but that would explain the behavior your are seeing. – Ole Begemann Jan 29 '11 at 16:08
  • Thanks, Ole. I think you're right. I'm going to look for a way to force the loading somehow. – CharlieMezak Jan 29 '11 at 16:14
  • I'm trying solutions like [this](http://stackoverflow.com/questions/1815476/cgimage-uiimage-lazily-loading-on-ui-thread-causes-stutter), but no luck yet. – CharlieMezak Jan 29 '11 at 16:40
0

If you use [UIImage imageNamed:], it'll do all this caching business for you. No need to roll your own cache and then wonder if it's working.

An upside and a downside to using that method. Upside: If you use the same image file twice, it won't actually load it twice, saving you that memory. Downside: Caching has a big memory impact, and you want to think pretty hard about doing it, whether you roll your own or not.

Dan Ray
  • 21,623
  • 6
  • 63
  • 87
  • Thanks for the reply! I'm loading the images from the documents directory, so imageNamed: isn't an immediate option. That's why I'm caching the images this way. However, I didn't consider that initWithContentOfFile ALSO caches the images, as Ole pointed out. Maybe I'm trying too hard here… – CharlieMezak Jan 29 '11 at 15:59