views:

975

answers:

3

I'm building an iPhone application where I detach some threads to do long-running work in the background so as not to hang the UI. I understand that threads need NSAutoreleasePool instances for memory management. What I'm not sure about is if the threaded method calls another method - does that method also need an NSAutoreleasePool?

Example code:

- (void)primaryMethod {
    [self performSelectorInBackground:@selector(threadedMethod) withObject:nil];
}

- (void)threadedMethod {
    NSAutoreleasePool *aPool = [[NSAutoreleasePool alloc] init];

    // Some code here

    [self anotherMethod];

    // Maybe more code here

    [aPool drain];
}

- (void)anotherMethod {
    // More code here
}

The reason I ask is I'm receiving errors that objects are being autoreleased with no pool in place, and are "just leaking."

I've seen other questions where people didn't have autorelease pools in place at all, and I understand why an autorelease pool is needed. I'm specifically interested in finding out whether an autorelease pool created in (in this example) threadedMethod applies to objects created in anotherMethod.

A: 

The autorelease pool does carry through to anotherMethod. However, when your threaded function ends, you should call [aPool release] instead of [aPool drain]. They are roughly equivalent, but aPool release causes the NSAutoreleasePool to release itself in addition to all the other objects in the pool. When your threaded function ends after calling drain, the autorelease pool still has a retain count of +1! Odds are, the "just leaking" object is aPool!

EDIT:

Jim Puls is right about release and drain being equivalent. The Apple docs clearly say they are identical in the non-garbage collected environment, and drain is better in the garbage collected case. My fault for not reading the docs!

Here's an article that presents a general overview of NSAutoreleasePools - it should help point you in the right direction. Since there is a virtual stack of autorelease pools, the topmost one will be used everywhere within your app - regardless of where objects are being autoreleased.

http://thegothicparty.com/dev/macos/nsautoreleasepool/

Ben Gotow
That's, um, not true: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html#//apple_ref/doc/uid/20000051-SW1
Jim Puls
To elaborate, autorelease pools aren't reference-counted themselves; they form a stack where autoreleased objects go into the topmost pool. -drain, -release, and the end of each run loop iteration just pop the stack of autorelease pools.
Jim Puls
Ben: does that also hold true across class methods? Say, if anotherMethod existed in AnotherClass, and I instantiated anotherClass within threadedMethod - does the code run in [anotherClass anotherMethod] still get autoreleased appropriately?
Tim
Yes, they will be released properly. You can think of the autorelease pool as an imaginary barrier at some point in your call stack. Everything that is autoreleased after the latest barrier (autorelease pool) is erected will be released when that barrier is torn down. You can follow the execution flow to see how it works. Note that if -anotherMethod were to create its own autorelease pool and drain it before exiting, any autoreleased objects created in that method would belong to the innermost pool, not the one in -threadedMethod.
Quinn Taylor
Thanks for the answers, Ben and Quinn!
Tim
+6  A: 

To answer your question, yes, anotherMethod is using the NSAutoreleasePool you created in threadedMethod, and anything you autorelease there will be released when aPool is released/drained.

So it is unlikely that your error is stemming directly from this code (unless there is more going on).

Put a break point on _NSAutoreleaseNoPool (add it by name in the Breakpoints window) and run your code in the debugger and it will stop when autorelease is called without a pool and that should resolve your problem.

Peter N Lewis
+1 for the breakpoint on _NSAutoreleaseNoPool suggestion. That helped eliminate the error messages.
Tim
A: 
  • Q: NSAutoreleasePool carrying across methods?
  • A: It depends:
    1. Across nested invocations, yes.
    2. Across sibling invocations, no.

And no matter what, the NSAutoreleasePool instance itself goes out of scope when the parent scope goes away. -in your example, at the very end of -(void)threadedMethod { }.

The article mentioned earlier (http://thegothicparty.com/dev/macos/nsautoreleasepool/) is quite clear about that.

gothic dev