Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dispatch_async and autoreleasepool

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?

like image 467
Boon Avatar asked Sep 15 '25 14:09

Boon


2 Answers

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
}
like image 90
Rob Avatar answered Sep 18 '25 05:09

Rob


At the risk of being less than completely helpful...

You should add @autoreleasepools 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.

like image 37
ipmcc Avatar answered Sep 18 '25 05:09

ipmcc