I am using dispatch_async that in turns call other code that uses dispatch_async, which uses for-loop to allocate at least 50 - 100 objects. How do I know when I should use @autoreleasepool? Can I just use @autoreleasepool in any dispatch_async that allocates more than 10 objects or is there a risk in using too many autoreleasepool?
As the Using Autorelease Pools section of the Advanced Memory Management Programming Guide advises to use autorelease pools where "either you must or it is beneficial to do so." I wouldn't generally advise just throwing in autorelease pools for no reason, though you can often do so without incident.
The @autoreleasepool
is not needed unless you're dealing with your own NSThread
instances (which you are not doing here), or if you are trying to explicitly reduce the high water mark caused by using many autorelease objects.
In terms of determining when you need autorelease pools to reduce peak memory utilization, it's not as simple as "I have ten objects." It's a question of the size of the objects and now many you have. If dealing with large images (e.g. 20000 x 20000 px), then more than one image may be too many. If dealing with small objects (strings, numbers, etc), then you can have thousands of objects without causing problems. And it only applies where you are using autorelease objects.
So, I'd suggest an empirical approach where you consider (a) at the peak amount of memory your routine uses; and (b) how many autorelease objects you're using, and make a decision from there.
It's worth noting, though, that there are some isolated situations where one must be careful adding an autorelease pool. The typical example is where you have a NSError **
parameter to a method for which you pass back an object if there was some error. For example, this code is problematic:
/** Some method that will optionally return error object.
*
* @param error The error object being returned
*
* @return This returns an NSData if successful, and `nil` upon error.
* See NSError object to determine why it failed.
*/
- (NSData *)someMethod:(NSError * __autoreleasing *)error
{
@autoreleasepool {
// do some stuff
// following line is wrong as the error will be released when pool is drained
if (failure)
*error = [NSError errorWithDomain:kMyAppDomain code:code userInfo:nil];
// etc
}
}
In that case, you might refactor it like so:
- (NSData *)someMethod:(NSError * __autoreleasing *)error
{
NSError *localError;
@autoreleasepool {
// do some stuff
// save it in some local variable outside of the scope of this block
if (failure) {
localError = [NSError errorWithDomain:kMyAppDomain code:code userInfo:nil];
}
}
// now you can return it safely
*error = localError;
// etc
}
At the risk of being less than completely helpful...
You should add @autoreleasepool
s when the Allocations template of Instruments shows you that your memory use during a particular workflow is significantly pushing up your application's high water mark (with autoreleased objects).
In other words, avoid premature optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With