3
votes

I wanted to easily blend a UIImage on top of another background image, so wrote a category method for UIImage, adapted from blend two uiimages based on alpha/transparency of top image :

- (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?

4

4 Answers

1
votes

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.

1
votes

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.

0
votes

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
-1
votes

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.