Using ARC, is it fatal not to have an autorelease pool for every thread?
Asked Answered
S

2

19

I read this:

If you ever create a secondary thread in your application, you need to provide it with its own autorelease pool. Autorelease pools and the objects they contain are discussed further in

in the iOS 5 Developer cookbook.

I'm compiling with ARC. I have been creating many background threads, and it seems that I am doing fine. None of my background threads are long-running. Will all those objects ever be released by say, the main thread's autorelease pool? Or what?

This is what I do to call background thread:

+(void)doBackground:(void (^)())block
{
    //DISPATCH_QUEUE_PRIORITY_HIGH
    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
    dispatch_async(dispatch_get_global_queue(-2,0), ^{
        block();
    });
}

Should I change that to

+(void)doBackground:(void (^)())block
{
    //DISPATCH_QUEUE_PRIORITY_HIGH
    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
    dispatch_async(dispatch_get_global_queue(-2,0), ^{
        @autoreleasepool{
        block();
        }
    });
}
Sperry answered 25/9, 2012 at 1:18 Comment(4)
are you using GCD, NSOperation or NSThread?Population
GCD. That's the industry standard right?Sperry
This quote from iOS documentation would help further "Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block."Cosmopolitan
Yes, this quote is from <Advanced Memory Management Programming Guide> developer.apple.com/library/archive/documentation/Cocoa/…Gil
U
35

Consider it at minimum a programmer error if you do not create an autorelease pool for your new thread. Whether that's fatal to your program is defined by your program's implementation. The classic problem is leaked objects and consequently objects' dealloc which is never executed (could be fatal).

The modern way to create an autorelease pool under ARC is:

void MONThreadsEntry() { // << entry is e.g. a function or method
  @autoreleasepool {
    ...do your work here...
  }
}

In more detail, autorelease pools behave as thread-local stacks -- you can push and pop, but there should always be one in place before anything on that thread is autoreleased. Autorelease messages are not transferred from one thread to another.

You may not see issues (e.g. in the console or leaks) if your idea of "creating a thread" is using a higher level asynchronous mechanism, such as using an NSOperationQueue, or if the underlying implementation creates a secondary thread and its own autorelease pool.

Anyways, rather than guessing when to create autorelease pools, just learn where you need to create them and when you should create them. It's all well-defined -- there is no need for guesswork, and there is no need to fear creating them.

Similarly, you will never need to create an autorelease pool for your thread if you are using lower level abstractions, and never autorelease objects on that thread. For example, pthreads and pure C implementations won't need to bother with autorelease pools (unless some API you use assumes they are in place).

Even the main thread in a Cocoa app needs an autorelease pool -- it's just typically not something you write because it exists in the project templates.

Update -- Dispatch Queues

In response to updated question: Yes, you should still create autorelease pools for your programs which run under dispatch queues -- note that with a dispatch queue, you're not creating threads so this is quite a different question from the original question. The reason: Although dispatch queues do manage autorelease pools, no guarantee is made regarding the time/point they are emptied. That is to say, your objects would be released (at some point), but you should also create autorelease pools in this context because the implementation could (in theory) drain the pool every 10,000 blocks it runs, or approximately every day. So in this context, it's really only fatal in scenarios such as when you end up consuming too much memory, or when your programs expects that its objects will be destroyed in some determined fashion -- for example, you could be loading or processing images in the background and wind up consuming a ton of memory if the life of those images is extended unexpectedly because of the autorelease pools. The other example is shared resources or global objects, where you could respond to notifications or introduce race conditions because your "block local" objects may live a lot longer than you expect. Also remember that the implementation/frequency is free to change as its implementors see fit.

Unamuno answered 25/9, 2012 at 4:17 Comment(6)
@JimThio i think you have misunderstood; i didn't say GCD is not creating threads, i said you are not creating threads. GCD dispatch queues operate more like thread pools than threads; it is not spawning a new thread for each task you submit. yes, GCD will create and manage threads behind the scenes, but the mechanics are (largely) abstracted from you. the steps you go through when setting up a thread which you create yourself explicitly (e.g. using pthreads or NSThread) is a different scenario/process. it's an important distinction because autorelease pools are thread local.Unamuno
I see. I mean so GCD will create the autorelease pool byitself right executed when the thread become idle or finish or whatever. I do not need to create my own autorelease pool as long as I use GCD.Sperry
@JimThio "…GCD will create the autorelease pool by itself" Correct. "I do not need to create my own autorelease pool as long as I use GCD." Well, no, that's not what I wrote in Update -- Dispatch Queues. I encouraged you to create them, even though you would not actually need to create them in the vast majority of cases. The problem is that you cannot dynamically determine when you would not need them. Many people who are happy with 98% coverage would just never create them for this reason. OTOH, a library developer with high standards might always create them.Unamuno
@JimThio Additional Notes: a) i once read that they did (internal detail) empty them after every task at one time, but again the implementation is free to change at any time. b) creating autorelease pools is very cheap on modern OS releases.Unamuno
+1 for both answers. This one is obviously far more comprehensive.Sperry
Is this still valid with iOS 8+?Saccharine
F
4

Seems now autorelease pool is created for new threads automatically. Don't know when this has changed and why documentation states opposite but that's it.

Fluecure answered 25/9, 2012 at 1:44 Comment(14)
Last I checked, the modern autoreleasepool mechanism would actually catch objects autoreleased without a pool in place, but would log the first time this happens for a given thread.Highness
What do you mean under "modern"? Previously messages were logged in this case something like "autoreleasing without pool in place, just leaking". I checked right now and there are no messages, objects are released correctly, and when I debug, there's pool draining occurring somewhere in thread_exit internals. This definitely differs from previous behaviour and from documentation. Something has changed recently...Fluecure
With the introduction of ARC, autorelease pools were rewritten. So this affects iOS 5.0 and later, and whatever OS X had ARC. Previously, you had a thread-lock stack of NSAutoreleasePool objects. Now, it's a different mechanism that can be thought of as a single array with sentinel values where the pool boundaries are. Since there's no more stack of discrete pool objects, every thread has an implicit "pool", but I thought it still tried to log the first time if you didn't use your own.Highness
Seems you're right. Where do you know this from? I wonder why official documentation on threading isn't updated to reflect these changes.Fluecure
I paid attention when ARC was introduced ;) And what documentation do you mean? If you mean the instruction to make sure you always create your own autoreleasepool when starting a new thread, that's still good advice because you really should be using a pool under your own control instead of assuming that implementation details of the OS will deal with it.Highness
Yes, I mean these instructions. Sure it's better to have own pool for each thread (and not only). But they could at least include this somewhere in release notes:) Anyway, thank you for the info.Fluecure
Well, it's an implementation detail. Documenting it would imply that you can rely on this in the future. But you shouldn't do that, because they could always rewrite the implementation again sometime later.Highness
Everything may change. But they're stating that each thread maintains its own stack of autorelease pools, and that without creating one for new threads objects will be leaked. If now it's not completely true, why not update with correct info and write smth like "it's strongly advised to create separate autorelease pools for each new thread".Fluecure
I don't choose answer yet. There are 2 completely different opinion and I still don't know which one is right.Sperry
Not completely different. I agree that you should create your own pools. Although currently pools are automatically provided both for GCD queues and for NSThreads and your objects will not leak in any case, it's good idea to create your own pools for parts of the code where many Cocoa objects are going to be created - not only in background threads but also during loops with many iterations, for example. Main UI thread pool is drained on every runloop iteration while system-provided pool for background thread will be drained not sooner than the thread exits.Fluecure
On the other hand, if you have only several lines of code in your background block, I think it's ok not to create pool. I like Obj-C memory management for being flexible and allowing developers to control what objects and when they want to be released.Fluecure
Oh I see. So pools are already created in GCD and NSThreads. Not like there is any other way to create background thread.Sperry
There are also POSIX threads of course. But it's unlikely that you will use them to call Cocoa code.Fluecure
See #24953049Berri

© 2022 - 2024 — McMap. All rights reserved.