views:

448

answers:

4

I wanted to easily blend a UIImage on top of another background image, so wrote a category method for UIImage, adapted from http://stackoverflow.com/questions/1309757/blend-two-uiimages :

- (UIImage *) blendedImageOn:(UIImage *) backgroundImage  {
 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 UIGraphicsBeginImageContext(backgroundImage.size);

 CGRect rect = CGRectMake(0, 0, backgroundImage.size.width, backgroundImage.size.height);
 [backgroundImage drawInRect:rect];
 [self drawInRect:rect];
 UIImage* blendedImage = [UIGraphicsGetImageFromCurrentImageContext() retain];

 UIGraphicsEndImageContext();
 [pool release];

 return [blendedImage autorelease];
}

Unfortunately my app that uses the above method to load around 20 images and blend them with background and gloss images (so probably around 40 calls), is being jettisoned on the device.

An Instruments session revealed that calls to malloc stemming from the calls to drawInRect: are responsible for the bulk of the memory usage. I tried replacing the drawInRect: messages with equivalent function calls to the function CGContextDrawImage but it didn't help. The AutoReleasePool was added after I found the memory usage problem; it also didn't make a difference.

I'm thinking this is probably because I'm not using graphics contexts appropriately. Would calling the above method in a loop be a bad idea because of the number of contexts I create? Or did I simply miss something?

- Edit 1: thanks for the comments. The method is invoked in a controller method that sets up 20 views, so in a loop I have the following call:

    UIImage *blendedImage = [newImage blendedImageOn:backgroundImage];

I added the autorelease pool to make sure the images are released before the main autorelease pool will release them, so theoretically all of the new UIImages objects should be released when the loop has finished. The profiling results don't show any difference whether the autorelease pool is in there or not.

- Edit 2: And yes, I've tried adding an autorelease pool before the call to blendedImageOn: as well, to no effect.

- Edit 3: fixed embarrassing bug of UIImage being released because of the autorelease pool. The intention of the autorelease pool is to release any objects other than the result UIImage, in case the excessive memory is due to temporary objects added to the main autorelease pool which are not freed immediately.

The question I was trying to ask (very badly, I admit!) is: why does calling this method 20 times result in a lot of memory usage?

+1  A: 

You shouldn't call drawRect: method directly. use [self setNeddsDisplay]; instead (it won't help you with this leak);

About your leak. remove everything about pool. Your method returns autoreleased UIImage object. Please paste code where you're using returned UIImage and I'll be able to help you. Very likely that you should make pool in place where you invoke blendedImageOn: and drain the pool each 3-5 iterations.

OgreSwamp
It's drawInRect, to write one image to another. Pretty sure setNeedsDisplay will not do that!
walkytalky
Sorry, my mistake, read drawRect: instead of drawInRect.Anyway 2nd part of the answer remains. It is better to show how author invokes blendedImageOn method.
OgreSwamp
True. +1 for catching the autoreleased object in the pool. (That sounds all wrong.)
walkytalky
A: 

One problem with your code is that the blendedImage object you get is marked for autorelease and when you release the temporary autorelease pool, poof, you release blendedImage too.

You could retain until until the pool is released, then return it autoreleased:

...
UIImage* blendedImage = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext();
[blendedImage retain];       // keep this past pool release
[pool release];

[blendedimage autorelease];  // now it will be autoreleased from the main pool

return [blendedImage release];  // release to balance out the retain above
progrmr
progrmr, good catch. I have in fact posted in-troubleshooting code that doesn't exhibit my symptoms but is demonstrating an entirely different memory problem as you pointed out. All, sorry for the mess :-(I've edited the code and re-phrased the problem description in the hopes it will clarify the problem I'm having.
A: 

I found the problem for the excessive memory usage. My images were much bigger than I'd anticipated, and resizing the background image to the view size before invoking the posted method that draws them into the graphics context, solved the memory usage.

I'm still puzzled why the memory isn't reclaimed when the image context is ended.

A: 

According to the iPhone memory usage guidelines, any method that returns an "owned" object (not autoreleased) should start with "alloc" or "new", or it should contain "copy".

Your name "blendedImageOn" doesn't follow these guidelines. If you use it in a month, or someone else uses it, you won't remember that the image is retained inside and you'll have a memory leak.

Adam Woś