views:

68

answers:

2

I drained an autorelease pool. The warning *** attempt to pop an unknown autorelease pool means the autorelease pool was created and drained in different methods - that's fine.

But does it mean such pool is NOT being drained? Is there a solution?

A: 

NSAutoreleasePools are stack objects, meaning that when they go out of scope, they can no longer release the objects they maintain. Creating an NSAutoreleasePool in one method and draining it in another is invalid code, and you're going to have to limit your pool to a single scope and context.

Further reading: NSAutoreleasePool (Apple Documentation), NSAutoreleasePool (Dev Blog).

Update: sorry. In my haste to answer your question, I misread the documentation. Apoligies to anyone I may have misled.

itaiferber
this answer is totally incorrect.
Dave DeLong
+3  A: 

@itaiferber is incorrect, as is the Dev Blog post he links to.

NSAutoreleasePools are not created on the Stack. They are allocated on the heap, just like all other Cocoa objects.

I think where the confusion comes from is that in the documentation it says:

Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects (see “Threads”). As new pools are created, they get added to the top of the stack. When pools are deallocated, they are removed from the stack.

The "stack" referred to in the documentation is not the call stack, but a stack data structure. This means that if you have "autorelease pool 1", and then create a second autorelease pool, and then autorelease an object, the object will be released when the second autorelease pool is drained.

In summary: you are perfectly welcome to create an autorelease pool in one method and drain it in another, as long as you understand the memory implications of doing so. (Whether or not this is bad design is another question entirely)

If you end up not draining the pool, it will be drained for you when a parent autorelease pool is drained.

If you release an autorelease pool that is not the top of the stack, this causes all (unreleased) autorelease pools above it on the stack to be released, along with all their objects. If you neglect to send release to an autorelease pool when you are finished with it (something not recommended), it is released when one of the autorelease pools in which it nests is released.

So if your autorelease pool stack looks like:

 _____
|  1  |  <--- most recently allocated pool
|-----|
|  2  |
|-----|
|  3  |  <--- least recently allocated pool
 -----

And you then drain pool 3, pools 2 and 1 will automatically be drained as well. I'm guessing this is what's happening in your code. You're creating "pool 1", and then it's getting automatically drained when "pool 3" gets drained, and then you're attempting to drain pool 1 yourself, but it's no longer valid, and you are "attempting to pop an unknown autorelease pool".

This problem because especially apparent if you're trying to hang on to autorelease pools in a GUI application. In such applications (UIKit or AppKit based), the run loop will create and destroy an autorelease pool on every pass of the loop, which means that any autorelease pool you create during an iteration of the loop will be destroyed the next time the loop comes around.

For much more specific information on this, there's an entire section of the Memory Management Programming Guide devoted to autorelease pools.

Dave DeLong
@Dave DeLong Yeah I've read the documentation entirely but it's not very clear. From my tests an autorelease pool seems NOT to drain when created in different scopes. I'm getting memory problems and the solution seems to be primitive: for every method, alloc a pool in the beginning of the scope and drain it in the end. I tried to alloc a NSAutoreleasePool in my main UIViewController and drain it with a repeating timer, but it hasn't worked.
GSchv
@GSchv the timer approach won't work, because many many many iterations of the run loop will pass in between when the pool is created and the timer next first, which means the pool will be destroyed prematurely. It would seem like you've got some fundamental organizational flaws in your app.
Dave DeLong
@Dave DeLong that's exactly what I'm talking about. I just needed confirmation that the pool only collects objects that are autoreleased in the same scope.
GSchv