Problem dealloc’ing memory used by UIImageViews with fairly large image in an UIScrollView

I’ve solved the mystery – and I’m pretty sure this is a bug on Apple’s side.

As Kendall suggested (thanks!), the problem lies in how InterfaceBuilder loads images from the NIB file. When you initFromNib, all UIImageViews will init with a UIImage using the imageNamed: method of UIImage. This call uses caching for the image. Normally, this is what you want. However, with very large images and additionally ones that scroll far off of the visible area, it does not seem to be obeying memory warnings and dumping this cache. This is what I believe to be a bug on Apple’s side (please comment if you agree/disagree – I’d like to submit this if others agree). As I said above, the memory used by these images does seem to be released if a user scrolls around enough to make it all visible.

The workaround that I’ve found (also Kendall’s suggestion) is to leave the image name blank in the NIB file. So you lay out your UIImageView elements as normal, but don’t select an image. Then in your viewDidLoad code, you go in and load an image using imageWithContentsOfFile: instead. This method does NOT cache the image, and therefore does not cause any memory issues with retaining large images.

Of course, imageNamed: is a lot easier to use, because it defaults to anything in the bundle, rather than having to find the path. However, you can get the path to the bundle with the following:

NSString *fullpath = [[[NSBundle mainBundle] bundlePath];

Putting that all together, here’s what that looks like in code:

NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:@"/%@-%d.png", self.nibName, imageView.tag]];
UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
imageView.image = loadImage;

So adding that to my code above, the full function looks like this:

- (CGFloat)findHeight 
{
    UIImageView *imageView = nil;
    NSArray *subviews = [self.scrollView subviews];

    CGFloat maxYLoc = 0;
    for (imageView in subviews)
    {
        if ([imageView isKindOfClass:[UIImageView class]])
        {
            CGRect frame = imageView.frame;

            if ((frame.origin.y + frame.size.height) > maxYLoc) {
                maxYLoc  = frame.origin.y;
                maxYLoc += frame.size.height;
            }

            NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:@"/%@-%d.png", self.nibName, imageView.tag]];
            NSLog(fullpath);


            UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
            imageView.image = loadImage;
        }
    }
    return maxYLoc;
}

Leave a Comment