Autorelease Pools and When Release Is Called Under iOS

Autorelease pools and when release is called under iOS

Yes, UIKit does the same thing. The system-created, main thread autorelease pool is drained at the end of every run loop cycle. It's probably best not to rely on this exact lifetime in your own code. If you create a new thread manually (using e.g. NSThread), you're responsible for creating the autorelease pool on that thread.

EDIT: Rob's answer provides some good additional information regarding behavior under ARC. In general, it's fair to say that objects are less likely to end up in the autorelease pool due to some optimizations ARC is able to make.

Autorelease pools in Objective-C - release main AutoreleasePool?

1) Do all autoreleased objects stay in that pool until the termination of the app?

Autoreleased objects by definition are owned by their autorelease pool until that pool is drained. When you send -autorelease to an object, that object is added to a list of objects that the pool will release later. Autorelease pools are organized in a stack, with the pool at the top of the stack being the pool to which any objects sent -autorelease are added. The pool created in main() is generally not the one at the top of the stack. For example, the run loop will create an autorelease pool at the beginning of each iteration.

2) If 1 is true, does creating an autoreleased object without a local
autorelease pool (therefore placing that object in the main.m pool)
keep that object in memory until termination of the app or a memory
warning is received?

It would if the pool created in main() were the topmost pool, but as described above, that usually won't be the case.

3) When is the main.m autorelease pool drained, other than when the
app receives a memory warning or the application is terminated?

There's no difference between the pool created in main() and any other autorelease pool. They're all drained when the pool is released, or at the end of the block if you used the @autorelease directive.

When do autorelease pools drain?

The documentation is not specific on when the "main" autorelease pool drains, but generally you can assume it is drained at the end of the application's main event loop.

Here's what happens with regards to autorelease pools:

  1. An autorelease pool is created when an application starts.
  2. When another pool is created, it is added to the top of the autorelease pool stack.
  3. When a autorelease is sent to an object, it is added to the autorelease pool at the top of the stack.
  4. When release is sent to an autorelease pool, it, in turn, sends release to any object in the pool.

#4 typically happens automatically (for the main autorelease pool) at the end of the main event loop.

The documentation for NSAutoreleasePool has more information, including this relevant tidbit:

The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.

End of run loop -- autorelease pool recovery

You have to understand the concept of a run-loop. The run loop in iOS waits for some event to happen and then it acts upon it. That event could be the user touching the screen, receiving a call, etc.

For every such event that iOS handles, a new autorelease pool is created at the beginning and drained when the event processing is complete. Theoretically there could be any number of nested autorelease pools created by Cocoa Touch, but the main one you should know about is the event loop.

Maybe this diagram from the Application Life Cycle will help.

UIKit event loop.

In pseudo-code, this boils down to,

int UIApplicationMain(...) {
while (!shouldQuitApplication) {
Event *someEvent = // wait for next event;
NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
// handle event
[myPool release];
}
}

These are the event types in iOS

UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,

So after every touch, motion, or remote control event is processed, the pool will be drained.

When autorelease pool release object in background thread?

https://developer.apple.com/documentation/foundation/nsautoreleasepool?language=objc

autorelease pool is just a unlimited stack for keeping autorelease object.
when you create a autorelease pool, pool stack push a watcher.
when you call autorelease on object, the object is pushed into the pool stack.
when you release autorelease pool, it release all pushed object after watcher, and then remove the watcher.

@autorelease in objc or autorelease in swift, is just a wrapper for create a autorelease pool, call block and then release pool.

runloop will automatically wrapper task into a autorelease pool.

but when you use autorelease pool with a custom thread, which haven't a runloop, in my observe, object will release when thread exit..

How to observe autorelease timeing

you can create a custom watch class with deinit defined, and manual retain and autorelease it, to observe the deinit timeing. code as below

class A {
deinit {
print("a dealloced")
}
}

var p: pthread_t?
_ = pthread_create(&p, nil, { (p) -> UnsafeMutableRawPointer? in
do {
let a = A()
_ = Unmanaged.passRetained(a).autorelease()
}
print("will exit pthread")
return nil
}, nil)
pthread_join(p!, nil)
print("finish")

this script will print

will exit pthread
a dealloced
finish

also you can breakpoint at deinit to see the backtrace of autorelease

Why is @autoreleasepool still needed with ARC?

ARC doesn't get rid of retains, releases and autoreleases, it just adds in the required ones for you. So there are still calls to retain, there are still calls to release, there are still calls to autorelease and there are still auto release pools.

One of the other changes they made with the new Clang 3.0 compiler and ARC is that they replaced NSAutoReleasePool with the @autoreleasepool compiler directive. NSAutoReleasePool was always a bit of a special "object" anyway and they made it so that the syntax of using one is not confused with an object so that it's generally a bit more simple.

So basically, you need @autoreleasepool because there are still auto release pools to worry about. You just don't need to worry about adding in autorelease calls.

An example of using an auto release pool:

- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
@autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(@"number = %p", number);
}
}
}
}

A hugely contrived example, sure, but if you didn't have the @autoreleasepool inside the outer for-loop then you'd be releasing 100000000 objects later on rather than 10000 each time round the outer for-loop.

Update:
Also see this answer - https://stackoverflow.com/a/7950636/1068248 - for why @autoreleasepool is nothing to do with ARC.

Update:
I took a look into the internals of what's going on here and wrote it up on my blog. If you take a look there then you will see exactly what ARC is doing and how the new style @autoreleasepool and how it introduces a scope is used by the compiler to infer information about what retains, releases & autoreleases are required.



Related Topics



Leave a reply



Submit