2
votes

Advanced Memory Management Programming Guide says regarding @autoreleasepool:

Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint

Many programs create temporary objects that are autoreleased. These objects add to the program’s memory footprint until the end of the block. In many situations, allowing temporary objects to accumulate until the end of the current event-loop iteration does not result in excessive overhead; in some situations, however, you may create a large number of temporary objects that add substantially to memory footprint and that you want to dispose of more quickly. In these latter cases, you can create your own autorelease pool block. At the end of the block, the temporary objects are released, which typically results in their deallocation thereby reducing the program’s memory footprint.

The following example shows how you might use a local autorelease pool block in a for loop.

NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {

    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
        /* Process the string, creating and autoreleasing more objects. */
    }
}

Can this code also be written without autoreleasepool and efficiently managed?

like creating a property of fileContents and setting it nil after processing it.

self.filecontents = nil;
3
This code can be written without ARP as ARC exists in iOS now.Ashwani
@Ashwin: As I understand it, autorelease pools are independent of manual vs. automatic reference counting. Even with MRP the code could be written without ARP (just replace fileContents = nil by [fileContents release] in my suggested answer below).Martin R
@Ashwin That is just plain wrong. The autorelease block format was introduced with ARC. Autoreleased objects within a loop will not be released until the run loop hits the next iteration (because UIKit runs each event in a autoreleasepool block), unless using an autoreleasepool.danielbeard
Thanks! @danielbeard for explanation and make me better understand.Ashwani

3 Answers

5
votes

The problem is that stringWithContentsOfURL can return an autoreleased object. But you could use initWithContentsOfURL instead:

NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
    NSError *error;
    NSString *fileContents = [[NSString alloc] initWithContentsOfURL:url
                                     encoding:NSUTF8StringEncoding error:&error];
    /* Process the string ... */
    fileContents = nil;
}

init... methods return a (+1) retained object and not an autoreleased object, therefore fileContents = nil releases the object and destroys it (if there are no other strong references to it).

Of course this would help only if the "string processing code" does not produce other autoreleased objects. (Also error, if set, would be an autoreleased object.)

(Actually it not "guaranteed" that stringWithContentsOfURL returns an autoreleased object. Especially in Release mode, the ARC compiler removes many unnecessary retain/autorelease/release operations.)

I don't know if establishing a local autorelease pool is an expensive operation or not (I assume not). If you process many objects in the loop and you don't know exactly whether autoreleased objects are created or not, it might be sensible to just use the local autorelease pool and "don't care about it". Profiling with "Instruments" could also give more insight.

For more information, see "Retained return values" and "Unretained return values" in the Clang ARC documentation.

1
votes

There's no difference in assigning to a strong property and then nil-ing it out and assigning to a strong local variable and having it go out of scope. The fundamental issue is that the object assigned to fileContents is placed in an autorelease pool that won't be drained at least until the for loop has finished iterating all the URLs. Putting the loop body inside an @autoreleasepool block causes fileContents to be autoreleased with each loop iteration.

0
votes

it seems like that on the surface, but any cocoa method call you make could potentially create an autoreleased object behind the scenes... as you can see if you perform same kind operation on another thread without an autorelease pool.

it is wise to add a separate pool if you have long running loop.

there is also a model that used to be more popular that goes like:

NSAutoreleasePool * pool = [NSAutoreleasePool new];
for (int i =0;i>3009;i++){
    //do stuff;
    if(!(i%100))
    {
        [pool drain],pool = [NSAutoreleasePool new];}
}
[pool drain];