GVKun编程网logo

为什么 ARC 仍然需要 @autoreleasepool?(arc为什么要关闭)

12

在本文中,您将会了解到关于为什么ARC仍然需要@autoreleasepool?的新资讯,同时我们还将为您解释arc为什么要关闭的相关在本文中,我们将带你探索为什么ARC仍然需要@autoreleas

在本文中,您将会了解到关于为什么 ARC 仍然需要 @autoreleasepool?的新资讯,同时我们还将为您解释arc为什么要关闭的相关在本文中,我们将带你探索为什么 ARC 仍然需要 @autoreleasepool?的奥秘,分析arc为什么要关闭的特点,并给出一些关于@autoreleasepool-内存的分配与释放、@autorelesepool、auto release pool 应该用 release 还是 drain、Autorelease Pools自动释放池概要的实用技巧。

本文目录一览:

为什么 ARC 仍然需要 @autoreleasepool?(arc为什么要关闭)

为什么 ARC 仍然需要 @autoreleasepool?(arc为什么要关闭)

在大多数情况下,使用 ARC(自动引用计数),我们根本不需要考虑 Objective-C
对象的内存管理。不再允许创建NSAutoreleasePools,但是有一种新语法:

@autoreleasepool {    …}

我的问题是,当我不应该手动释放/自动释放时,为什么我需要这个?


编辑: 总结一下我从所有答案和评论中得到的简洁:

新语法:

@autoreleasepool { …}是新的语法

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];…[pool drain];

更重要的是:

  • ARCautoreleaserelease.
  • 它需要一个自动释放池来执行此操作。
  • ARC 不会为您创建自动释放池。 然而:
    • 每个 Cocoa 应用程序的主线程中已经有一个自动释放池。
  • 有两种情况您可能想要使用@autoreleasepool
    1. 当你处于辅助线程并且没有自动释放池时,你必须自己制作以防止泄漏,例如myRunLoop(… { @autoreleasepool { … return success; }.
    2. 当您希望创建一个更本地的池时,正如@mattjgalloway 在他的回答中所示。

答案1

小编典典

ARC 并没有摆脱保留、发布和自动发布,它只是为您添加所需的内容。所以仍然有保留的调用,仍然有释放的调用,仍然有自动释放的调用,还有自动释放池。

他们使用新的 Clang 3.0 编译器和 ARC
进行的其他更改之一是它们替换NSAutoReleasePool@autoreleasepool编译器指令。NSAutoReleasePool无论如何,它总是有点特殊的“对象”,他们这样做是为了使使用
one 的语法不会与对象混淆,因此它通常更简单一些。

所以基本上,你需要@autoreleasepool因为仍然需要担心自动释放池。您只是无需担心添加autorelease呼叫。

使用自动释放池的示例:

- (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);            }        }    }}

当然,这是一个非常人为的例子,但是如果您没有@autoreleasepool外部循环内部,for那么您稍后将释放 100000000
个对象,而不是每次围绕外部循环释放 10000 个对象for

更新: 我查看了这里发生的内部情况并将其写在我的博客上。如果您看一下那里,您将确切地看到 ARC
正在做什么,以及@autoreleasepool编译器如何使用新样式以及它如何引入范围来推断有关需要保留、发布和自动发布的信息。

@autoreleasepool-内存的分配与释放

@autoreleasepool-内存的分配与释放

@autoreleasepool-内存的分配与释放

开发过iOS、Mac的朋友应该对“ @autoreleasepool ”不陌生。只要在Xcode里创建一个工程,就能看到下面这样的代码:

//iOS program
int main(int argc, char * argv[]) {
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  }
}
//Command line program
int main(int argc, const char *argv[]) {
  @autoreleasepool {
    //...
  }
  return 0;
}

看,每个 main 函数的主体都被 @autoreleasepool 的Block块包在里面,也就是说,接下来所有的对象创建都在这个block里面。

那么, @autoreleasepool 的作用到底是什么呢?我们开发中可以用它来做什么呢?

可以在某些情况下,大幅度降低程序的内存占用,如下图:


测试的内容:500000次循环,每次循环创建一个NSNumber实例和两个NSString实例。
图:红线表示没有用 @autoreleasepool 时的内存占用。
图:绿线表示用了 @autoreleasepool 优化后的内存占用!
效果是不是很明显!

代码Github地址: AutoReleasePoolTestExample Xcode 6, iOS 8, iPhone 5模拟器.

MRC 与 ARC

MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting),分别对应着手动引用计数和自动引用计数。

对!是计数,不是“ GC、垃圾回收 ”什么的,就是说,在Objective-C的开发中,ARC不代表像Java那样有GC做垃圾回收,所以本质上还是要“手动”管理内存的。也就是说,我们在ARC环境下写的代码,不用自己手动插入“ retain、release这些消息 ”,ARC会在编译时为我们在合适的位置插入,释放不必要的内存。

而 @autoreleasepool 就跟对象的 release 密切相关。

@autoreleasepool 干了啥

在MRC时代,如果我们想先retain一个对象,但是并不知道在什么时候可以release它,我们可以像下面这么做:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSString* str = [[[NSString alloc] initWithString:@"tutuge"] autorelease];
//use str...

[pool release];
//str is released
就是说,我们可以在创建对象的时候给对象发送“ autorelease ”消息,然后当 NSAutoreleasePool 结束的时候,“标记过”autorelease的对象都会被“ release ”掉,也就是会被释放掉。

但是在ARC时代,我们不用手动发送 autorelease 消息,ARC会自动帮我们加。而这个时候, @autoreleasepool 做的事情,跟 NSAutoreleasePool 就一模一样了。

什么时候用@autoreleasepool

根据 Apple的文档 ,使用场景如下:

写基于命令行的的程序时,就是没有UI框架,如AppKit等Cocoa框架时。
写循环,循环里面包含了大量临时创建的对象。(本文的例子)
创建了新的线程。(非Cocoa程序创建线程时才需要)
长时间在后台运行的任务。
利用@autoreleasepool优化循环

利用@autoreleasepool优化循环的内存占用,我觉得最有用的一点,下面就说说这个点。

如下面的循环,次数非常多,而且循环体里面的对象都是临时创建使用的,就可以用 @autoreleasepool 包起来,让每次循环结束时,可以及时的释放临时对象的内存。

//来自Apple文档,见参考
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
  @autoreleasepool {
    NSError *error;
    NSString *fileContents = [NSString stringWithContentsOfURL:url
                     encoding:NSUTF8StringEncoding error:&error];
    /* Process the string, creating and autoreleasing more objects. */
  }
}

这么做的效果是极其显著地,就如本文最开始的图一样,可以自己把示例工程下回来运行下试试~

总结

@autoreleasepool看起来很不“起眼”,平常开发很容易就忽略它了,但是仔细一看,确如此有用~

参考

@autorelesepool

@autorelesepool

  • MRC,
  • 在这种情况下@autorelesepool块等同于调用NSAutoreleasePool类的api
  • Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
  • 对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。
    那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。Autorelease是保证一个method安全的,对于method中的函数调用也适用。


  • ARC,将释放所有在@autorelesepool块中的对象,这就是为什么本文示例使用了默认修饰符(strong),相当于做了retain,也一样被释放的原因
  • 当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置 时,自动释放池就会被释放,池中的所有对象也就被释放。



参考文档:http://blog.sunnyxx.com/page/11/

http://blog.csdn.net/itianyi/article/details/8918183

auto release pool 应该用 release 还是 drain

auto release pool 应该用 release 还是 drain

文档里是这么说的:

drain

In a reference-counted environment, releases and pops the receiver; in a garbage-collected environment, triggers garbage collection if the memory allocated since the last collection is greater than the current threshold.

- (void)drain
Discussion

In a reference-counted environment, this method behaves the same as release. Since an autorelease pool cannot be retained (see retain), this therefore causes the receiver to be deallocated. When an autorelease pool is deallocated, it sends a release message to all its autoreleased objects. If an object is added several times to the same pool, when the pool is deallocated it receives a release message for each time it was added.

In a garbage-collected environment, this method ultimately calls objc_collect_if_needed.

Special Considerations

In a garbage-collected environment, release is a no-op, so unless you do not want to give the collector a hint it is important to use drain in any code that may be compiled for a garbage-collected environment.

对于 iOS 来说 drain 和 release 的作用其实是一样的。

Autorelease Pools自动释放池概要

Autorelease Pools自动释放池概要

本篇将给您介绍”Autorelease Pools”(自动释放池)在应用中的使用技巧。

1,Autorelease Pools概要

一个”Autorelease Pool”实例中“包含”其它各种调用了”autorelease”方法的对象。当它释放时,其中所有被管理对象都会收到”relrease”的消信。注意,同一个对象可以被多次调用”autorelease”方法,并可以放到同一个”Autorelease Pool”中。引入这个自动释放池机制,对象的”autorelease”方法代替”relrease”方法可以延长它的生命周期,直接到当前”Autorelrease Pool”释放。如果想让此对象的生命周期超过”Autorelease Pool”,还可以再次”retain”,呵呵,有意思吧〜且让我慢慢道来。

Cocoa总是认为当前至少有一个”Autorelease Pool”对象是可用的。若此对象并不存在,你调用的”autorelease”的所有对象都不会被自动释放掉,可想而知,造成内存泄露。Cocoa把这个错误信息写入日志〜〜仅仅是为了以后分析。

你可以用”alloc”与”init”方法创建一个”NSAutoreleasePool”对象,并且可以调用”release”或”drain”(”release”与”drain”的区别是”drain”在有GC的环境中会引起GC回收操作,”release”反之。但在非GC环境中,两者相同。官方的说法是为了程序的兼容性,应该考虑用”drain”代替”release”,)方法来回收它(调用它的”autorelease”或”retain”方法会引起异常)。在一个完整的上下文最后”Autorelease Pool”对象应该被”release”掉(在方法内或一段循环体内创建的”Autorelease Pool”对象)。

“Autorelease Pools”的所有实例在栈中管理(我们暂时叫他“自动释放池栈”),并且它们是可以被嵌套的(父生子,子生孙。。。子子孙孙 ^_^)。例如,当我们创建一个”Autorelease Pool”对象后,它就被自动放到“自动释放池栈”的栈顶。当本池对象回收时,它就随之从这个栈中POP掉。那么也就是说,当任何一个对象调用”autorelease”方法后,它会被放入当前线程中当前栈顶的自动释放池中。

接下来我们聊聊”Autorelease Pools”的嵌套问题。在你的应用中,你可以任意多的创建”Autorelease Pool”对象,而这些对象被当前线程的“自动释放池栈”所管理。那么除了一个接一个的顺序创建并销毁它的情况外,还有一种使用方式,就是嵌套式的创建与使用。例如:在你的主函数创建了一个”autorelease pool”,然后又调用了创建了”autorelease pool”实例的其它方法;或是在外循环中创建了”Autorelease Pool”的实例,而内循环中也做了相同的事情。有意思吧,呵呵,嵌套的机制使父Pool实例释放后,它的所有子Pool也将释放。但这里还存在一些副作用,后续文章会详细讨论。

“Application kit”在一个事件循环里会自动创建一个”autorelease pool”。像鼠标键的按下与释放,所以你编写的代码通常不需要考虑太多这方面的事情。当然,有以下三种情况你会创建与销毁自己的Pool实例:

    1,应用不是基于”Application Kit”,像”Command-line tool”,因为它并没有内置的”autorelease pools”的支持。
    2,创建线程,你必需在线程开始时创建一个”Autorelease Pool”实例。反之,会造成内存池露(会在以后的文章详细说明线程与池的技巧)。
    3,一个循环内创建了太多的临时对象,你应该为他们创建一个”Autorelease Pool”对象,并在下次循还前销毁它们。

2,自动释放池中的”Non-AppKit”应用 在”Non-AppKit”应用中使用自动释放池的机制其实是相当简单的事情。你仅仅需要在main()起始处创建”Autorelease Pool”对象,并在结尾处释放掉它。就像在Xcode的Foundation Tool的创建模版里写的一样。这个确保你在应用生命周期内至少有一个”Autorelease Pool”是可用的。但是,这也使所有在此期间的所有”autorelease”的对象都必需在应用结束后才被释放。这也许会引起在应用的使用中不断的增长,所以,你仍然考虑在不同的作用域创建新的”Autorelease Pool”。 大多应用中都存在各种级别的循环机制。在这些应用中,你可以在每个循环内的开头创建一个”Autorelease Pool”对象,并在结尾处释放掉它。 例如: void main() {     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];       NSArray *args = [[nsprocessInfo processInfo] arguments];     unsigned count,limit = [args count];       for (count = 0; count < limit; count++)     {         NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];         Nsstring *fileContents;         Nsstring *fileName;           fileName = [args objectAtIndex:count];         fileContents = [[[Nsstring alloc] initWithContentsOfFile:fileName] autorelease];         // this is equivalent to using stringWithContentsOfFile:           /* Process the file,creating and autoreleasing more objects. */           [loopPool release];     }       /* Do whatever cleanup is needed. */     [pool drain];       exit (EXIT_SUCCESS); } 在命令行中处理所有以参数传来的文件。一次循环处理一个文件。在循环的开头创建一个”NSAutoreleasePool”对象,并在循环结束时释放掉。因此,任何在其中创建并调用“autorelease”的对象都将添加到这个Pool实例中,当本池被释放后,这些对象也将被回收。注意,任何在作用域内创建的”autoreleased”对象(像”fileName”),虽然并没有显示的调用”autorelease”方法,但都将被当前池所管理并释放。 先到这里,休息,休息一下〜〜 本文翻译自: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-SW2

今天关于为什么 ARC 仍然需要 @autoreleasepool?arc为什么要关闭的介绍到此结束,谢谢您的阅读,有关@autoreleasepool-内存的分配与释放、@autorelesepool、auto release pool 应该用 release 还是 drain、Autorelease Pools自动释放池概要等更多相关知识的信息可以在本站进行查询。

本文标签: