自动释放池以及在iOS下调用发行版的时间

我想澄清一些事情。

比方说,我有以下代码:

- (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; for (int i = 0; i < 5000000; i++) { NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"]; } } 

这将在此函数调用中创build500万个自动发布的string。 我期待这个保留这些对象,直到应用程序终止,因为我看到的唯一的@autoreleasepool是在main.m中包装应用程序实例的一个。 但事实并非如此。 在这个函数调用的结尾,似乎他们都获得了他们的发布,并从内存中删除。

这个文件:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

“应用程序工具包在事件循环的每个循环开始时在主线程上创build一个自动释放池,并在最后耗尽它,从而释放处理事件时生成的任何自动释放对象。

这对我来说是有道理的,但这是在UIKit下,而不是Application Kit。 我的问题是,在这种情况下,UIKit / Cocoa Touch是否做了同样的事情,还是有我的对象获得释放的另一种解释?

谢谢!

是的,UIKit做同样的事情。 系统创build的主线程自动释放池在每个运行循环周期结束时被排空。 在自己的代码中最好不要依赖这个确切的生命周期。 如果你手动创build了一个新的线程(例如使用NSThread),你需要负责在该线程上创build自动释放池。

编辑:Rob的答案提供了一些关于ARC下的行为的好的附加信息。 一般来说,可以公平地说,由于ARC能够做出一些优化,所以对象不太可能最终进入自动释放池。

安德鲁回答了你的主要问题,是的,你的自动释放池将在主运行循环的每个循环中耗尽。 因此,当您返回到主运行循环时,在viewDidLoad创build的任何autorelease对象可能会立即被耗尽。 他们肯定不会被保留“直到申请终止”。

但是我们应该小心:你很明显地认为这些对象被添加到自动释放池中。 这个假设的一些注意事项:

  1. 在过去(并且仍然需要ARC-MRC互操作性),当从名称不是以allocnewcopymutableCopy开始的方法返回对象时,这些对象会自动释放对象,只有当自动释放池被排空(即当你退回到运行循环)。

  2. 但是,ARC已经变得更聪明,最大限度地减less了对autorelease池的需求(请参阅http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease ,其中讨论了callerAcceptsFastAutorelease ,现在称为callerAcceptsOptimizedReturnprepareOptimizedReturn调用),所以你经常会没有看到这个autorelease行为。 因此,如果库和调用者都使用ARC,对象可能不会被放置在自动释放池中,而是ARC会在不需要的时候立即释放它们。

    随着当代ARC项目,autorelease池通常是不需要的。 但是在某些特殊情况下,仍然可以使用autorelease池。 我将在下面概述其中的一种情况。

考虑下面的代码:

 #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 

下面的代码会将500,000个对象添加到autorelease池中,当我退回到运行循环时,这些对象只会被耗尽:

没有游泳池

在这种情况下,您可以使用自动释放池来尽量减less高水位:

 @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 

池

底线,使用ARC,使用autorelease对象并不总是显而易见,当variables超出范围时,它显式释放它。 您始终可以通过检查“乐器”中的行为来确认这一点。

NSString ,在使用NSString类的时候,我会对使用一般的内存pipe理结论提出NSString ,因为它经过了高度优化,并不总是符合标准的内存pipe理实践。

我会假设,当你分配一个新的对象引用,以保存一个对象,然后原来的对象是立即释放 (如果没有别的指向它 – ref计数为零)使用ARC和asuming默认strong引用循环的例子。

 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 

苹果笔记在转换到ARC发行说明

尝试停止考虑保留/释放调用的位置,并考虑应用程序algorithm。 考虑对象中的“强弱”指针,关于对象所有权以及可能的保留周期。

这听起来是在对象被赋予新的值时从对象上调用这个release 。Clang 3.4文档OBJECTIVE-C自动引用计数(ARC)

在评估赋值运算符时发生赋值。 语义根据资格而有所不同:

对于__strong对象,首先保留新的指针对象; 其次,左值加载了原始语义; 第三,新的pointee用原始语义存储在左值中; 最后,旧的指针被释放。 这不是自动执行的; 面对并发的加载和存储,必须使用外部同步来保证安全。