What are the consequences of omitting @autorelease

2020-06-16 05:15发布

问题:

Why do the Xcode 4.x templates for Objective-C command-line and iOS programs add the @autoreleasepool {} part wrapping main()'s code? Note that this doesn't happen for the OS X application template.

Why don't OS X applications do the same? Why don't both use the same method?

Finally, since all memory is released when any program exits, why is all of this of practical importance?


Or to ask it differently, what are the practical consequences of omitting @autoreleasepool { ... } in main() for a command line or an iOS Objective-C program?

These two pieces of code compile and seem to work equivalently:

1.

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSArray *array = @[@"Hello, world!"];
        NSLog(@"%@", array[0]);
    }
    return 0;
}

2.

int main(int argc, const char * argv[])
{
    NSArray *array = @[@"Hello, world!"];
    NSLog(@"%@", array[0]);
}

Note, I only care about the explanation in the ARC context. ARC forbids the explicit use of autorelease.

回答1:

autorelease doesn't work if there is no autorelease pool on the stack.

It's not really necessary to use autoreleased objects in objective-c (as you do in your examples), so you can omit it in theory, however most Apple frameworks do use autoreleased objects heavily.

Normally, every thread should have at least one autorelease pool, otherwise using any Obj-C code is very unsafe. Setting up an autorelease pool at the beginning of main is then a very good practice.

EDIT:

In ARC, although explicit autorelease is forbidden, autorelease calls are still there (added by the compiler). That implies the need for an autorelease pool.

This has nothing to do with releasing the memory. The very existence of an autorelease pool is necessary. Even if it isn't ever drained.

I guess that OS X doesn't add the autorelease pool to the template because the programmers can also use a garbage collector (although it is deprecated now).

EDIT 2:

Just created an OS X project and the @autoreleasepool is there. In fact, the only template without it is a "Core Foundation" project which is not really Obj-C, it's pure C.

EDIT 3: (After some more thinking and some googling)

With the introduction of ARC, autorelease pools were rewritten. When before they were a framework feature, now they are a language (Obj-C) feature. They are implemented differently. It seems that every new thread has an implicit autorelease pool now. Using @autoreleasepool doesn't actually create a new autorelease pool on some thread stack any more, it just puts a mark to the implicit autorelease pool (so that you can drain everything autoreleased after the mark). That means there is no way to create an example triggerring warnings or errors when @autoreleasepool is omitted.

However, this is considered to be an implementation detail, so it can be easily changed in future (or when another compiler is used!). That's why it's good practice to still set up an @autoreleasepool for every new thread (mentioned, for example, in -[NSThread detachWithSelector:..] documentation).



回答2:

In your code example above, you're not using any autoreleased objects whatsoever.

But, if you were doing something like this:

NSString * somethingToSay = [NSString stringWithString:@"this is an autoreleased string, believe it or not"];
NSLog(@"%@", somethingToSay);
return 0;

The "@autoreleasepool" bit would actually be a bit more useful.

Looking at Apple's documentation for autoreleasepool, they say:

At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.

So when you have lots of autoreleased objects (and there are plenty that you can create that end up as NSArray, NSDictionary, NSString, etc.), these autorelease pools help to keep memory freed up.

The application template includes "@autoreleasepool" in main.m because on devices like the iPhone or iPad, memory usage is critical and we'd want to make certain that all memory gets properly released if the application exits the "UIApplicationMain" (not very likely, since most people leave their iPhone apps running until rebooting, but it could happen).



回答3:

You need it because the documentation says you need it. That should be enough, trust the documentation:

Typically, you don’t need to create your own autorelease pool blocks, but there are some situations in which either you must or it is beneficial to do so.

...

Cocoa always expects code to be executed within an autorelease pool block, otherwise autoreleased objects do not get released and your application leaks memory. (If you send an autorelease message outside of an autorelease pool block, Cocoa logs a suitable error message.)

... There are, however, three occasions when you might use your own autorelease pool blocks:

  • If you are writing a program that is not based on a UI framework, such as a command-line tool.
  • ...
  • ...

...

If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.

If there is no autorelease pool... then most things will still work fine, but it is "most" things, not "all" things. They only state two reasons, however there might be other reasons that aren't documented and all of this behaviour is subject to change at any time in the future.

If you follow the recommended best practices, then your code should still work perfectly 20 years from now, but if you don't it might crash randomly once in every fifty executions starting with the next x.x.x update of the operating system.



回答4:

Probably this quote from Apple documentation would help:

If your detached thread does not make Cocoa calls, you do not need to use an autorelease pool block.

It could be extrapolated to all threads even master one (where "main" is).