Autorelease pools and when release is called under iOS

倖福魔咒の 提交于 2019-11-28 07:02:00

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.

Andrew answered your main question that, yes, your autorelease pool will be drained on every cycle of the main run loop. So any autorelease objects created in viewDidLoad may promptly get drained when you yield back to the main run loop. They certainly won't be retained "until the termination of the application."

But we should be careful: You're clearly assuming that these objects are being added to an autorelease pool. A few caveats to this assumption:

  1. In the past (and still required for ARC-MRC inter-operability), when returning objects from methods whose names did not start with alloc, new, copy, or mutableCopy, those objects would autorelease objects, deallocated only when the autorelease pool was drained (i.e. when you yielded back to the run loop).

  2. But ARC has gotten smarter about minimizing the need for autorelease pools (see http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease, which discusses callerAcceptsFastAutorelease, now called callerAcceptsOptimizedReturn invoked by prepareOptimizedReturn), so you often will not see this autorelease behavior. So, if both the library and the caller are using ARC, objects may not be placed in the autorelease pool, but rather ARC will cleverly release them immediately if they're not needed.

    With contemporary ARC projects, autorelease pools are generally not needed. But certain special cases, one can still benefit from using autorelease pools. I'll outline one of those cases below.

Consider the following code:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

The following code will add 500,000 objects to the autorelease pool, which will only be drained when I yield back to the run loop:

In this case, you might use an autorelease pool to minimize the high water mark:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

Bottom line, with ARC, it's not always obvious when it used an autorelease object and when it explicitly releases it when the variable falls out of scope. You can always confirm this by examining the behavior in Instruments.

As an aside, I'd be wary about drawing too many general memory management conclusions when using the NSString class, as it has been highly optimized and doesn't always conform to standard memory management practices.

I would assume that when you assign new object to a reference that used to hold an object then the original object is released right away (if nothing else points to it - ref count goes to zero) using ARC and asuming default strong reference as in your loop example.

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

Apple notes in Transitioning to ARC Release Notes

Try to stop thinking about where the retain/release calls are put and think about your application algorithms instead. Think about “strong and weak” pointers in your objects, about object ownership, and about possible retain cycles.

It sounds that the release is called on object when it's assigned new value, from clang Clang 3.4 documentation OBJECTIVE-C AUTOMATIC REFERENCE COUNTING (ARC)

Assignment occurs when evaluating an assignment operator. The semantics vary based on the qualification:

For __strong objects, the new pointee is first retained; second, the lvalue is loaded with primitive semantics; third, the new pointee is stored into the lvalue with primitive semantics; and finally, the old pointee is released. This is not performed atomically; external synchronization must be used to make this safe in the face of concurrent loads and stores.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!