Phill Campbell
Phill Campbell

Reputation: 1305

Understanding NSAutoreleasePool

I have an app that gets a memory warning when using the camera on an iPhone 4s. I scale the image before I use it.

+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{

// Create a graphics image context
UIGraphicsBeginImageContext(newSize);

// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];

// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

// End the context
UIGraphicsEndImageContext();


// Return the new image.
return newImage;
}

I read that you should use an NSAutoreleasePool from here http://wiresareobsolete.com/2010/08/uiimagepickercontroller/

So I modified the code like this:

+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
//http://wiresareobsolete.com/2010/08/uiimagepickercontroller/

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// Create a graphics image context
UIGraphicsBeginImageContext(newSize);

// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];

// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

// End the context
UIGraphicsEndImageContext();

[newImage retain];

[pool release];

// Return the new image.
return newImage;
}

The memory warning went away. I did not call this on a separate thread. What is this NSAutoreleasePool doing? I just don't understand it.

Can I retain an object in a local NSAutoreleasePool?

Can I use the retained object after the NSAutoreleasePool has been released?

The important question: How does this specific usage of an NSAutoreleasePool help the memory footprint of my app so that it doesn't get memory warnings?

Upvotes: 1

Views: 2158

Answers (2)

newacct
newacct

Reputation: 122419

First of all, you should be using @autoreleasepool blocks instead of NSAutoreleasePool objects; the latter is pretty outdated. The former is nicer because it automatically drains the pool when you leave the scope of the block without you needing to explicitly do it yourself.

An autorelease pool simply remembers the things that have been "autoreleased" while in its scope, and releases them when the pool is drained. "Autoreleasing" an object is like releasing it, but deferred; from a memory management perspective, it takes a strong reference that you have and "transfers" it to the pool, so that the pool now has a strong reference to the object and you don't. In pre-ARC, if a method (whose name did not start with new or copy) needs to return an object that was created during the function, it pretty much had to be autoreleased. By putting it on the pool, it guarantees that the object will be alive to be received by the calling function. In your code, UIGraphicsGetImageFromCurrentImageContext() will likely return an autoreleased object.

Autorelease pools only drain at the end of the pool. The objects in the pool are kept alive for the duration of the pool (because they are effectively "owned" by the pool). That means if the pool lasts for a long time and lots of things get autoreleased on it, then lots of objects are prevented from being deallocated, which is potentially bad.

For example, say one run of your function autoreleases one object (i.e. the object returned by UIGraphicsGetImageFromCurrentImageContext()). Then if you run your function 100,000 times in a loop, then 100,000 objects stay in memory, because they get put onto the same pool that hasn't had a chance to drain yet. However, if you put another level of pool into each iteration of the loop, it will drain at the end of each iteration, preventing objects from building up.

As for your second piece of code, putting an autorelease pool inside your simpleImageWithImage: method does catch the autorelease from UIGraphicsGetImageFromCurrentImageContext(). However, you have another problem because, in order to return the image object from the simpleImageWithImage: method itself, you need to autorelease it!

Your method, as written, violates the memory management rules. Your method returns a retained object, which the caller will have to remember to release. However, the caller doesn't know that based on the name. According to the naming conventions, the only methods that can return a retained instance are those whose names start with alloc, retain, new, copy, and mutableCopy. Your method doesn't start with any of these, so it must return a non-retained instance. Since you own the object (by retaining it), you must do a balancing release or autorelease. You can't release it because it would cause the object to be potentially deallocated since there are no other strong references to it, so you can only autorelease it.

But if you are going to autorelease it again, you have accomplished nothing by adding an autorelease pool here, unless inside drawInRect: or inside UIGraphicsGetImageFromCurrentImageContext() they autorelease a lot of stuff.

Upvotes: 7

simalone
simalone

Reputation: 2768

NSAutoreleasePool that created by yourself just invokes release method for the autorelease objects in its area where from NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; to [pool release];,

Here

UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

newImage is an autorelease object, [newImage release] will be called after [pool release];

If not [newImage retain]; newImage will dealloc after [pool release];

  1. You can retain an object in a local NSAutoreleasePool.
  2. You can use the retained object after the NSAutoreleasePool has been released.
  3. Use NSAutoreleasePool to release temporary memory.

Upvotes: 0

Related Questions