前言
当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池 定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
ojc-c 是通过一种"referring counting"(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有alloc,new,copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
NSAutoreleasePool 就是用来做引用计数的管理工作的,这个部分后面会详细说到.
autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.
4.设定项目编译环境为ARC下时,编译器会帮助我们在程序的入口main函数就调用NSAutoreleasePool,这样保证程序中不调用NSAutoreleasePool,但在退出时自动释放
1.NSAutoreleasePool是什么?
NSAutoreleasePool实际上是个对象引用计数自动处理器,在官方文档中被称为是一个类。
NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一 个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消 息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。
2.NSAutoreleasePool可以用来做什么,怎么用?
NSAutoreleasePool可以在一定程度上帮助我们苹果开发程序员管理内存,让我们的工作更加严密,简便。
1)在ARC项目中,系统会自动帮助我们在程序中嵌入NSAutoreleasePool,此为苹果公司程序员在写这个编译器的时候设定的;
2)在MRC项目中,我们需要自己去创建NSAutoreleasePool类对象去帮助我们管理内存;
3)使用应注意:
a.在ARC项目中我们同样可以创建NSAutoreleasePool类对象去帮助我们更精确的管理内存问题。
b. NSAutoreleasePool的管理范围是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];与[pool release];之间的对象
c.既然ARC项目中设置了ARC,为什么还要使用@autoreleasepool?(注意a的案例解释)
ARC 并不是舍弃了 @autoreleasepool
,而是在编译阶段帮你插入必要的 retain
/release
/autorelease
的代码调用。
所以,跟你想象的不一样,ARC 之下依然是延时释放的,依然是依赖于 NSAutoreleasePool
,跟非 ARC 模式下手动调用那些函数本质上毫无差别,只是编译 器来做会保证引用计数的正确性。
参考: Retain count semantics in ARC
What's @autoreleasepool
d.NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
当执行[pool autorelease]的时候,系统会进行一次内存释放,把autorelease的对象释放掉,如果没有NSAutoreleasePool , 那这些内存不会释放
注意,对象并不是自动被加入到当前pool中,而是需要对对象发送autorelease消息,这样,对象就被加到当前pool的管理里了。当当前pool接受到drain消息时,它就简单的对它所管理的所有对象发送release消息。(如例子1)e.在ARC项目中.不能直接使用autorelease pools,而是使用@autoreleasepool{},
@autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的时候也可以使用(autorelease嵌套)
4)使用例子:
例子1:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* nsstring;
char* cstring = "Hello CString";
nsstring = [NSString stringWithUTF8String:cstring];
[pool release];(这一行代码就是在给pool发送drain消息了)
3.autorelease的原理是什么?
Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当 前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
4.!!!autorelease何时释放?
对于autorelease pool本身,会在如下两个条件发生时候被释放(详细信息请参见第5条)
1)手动释放Autorelease pool
2)Runloop结束后自动释放
对于autorelease pool内部的对象在引用计数的retain == 0的时候释放。release和autorelease pool 的 drain都会触发retain--事件。
5、autorelease释放的具体原理是什么?
要搞懂具体原理,则要先要搞清楚autorelease何时会创建。
我们的程序在main()调用的时候会自动调用一个autorelease,然后在每一个 Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的 Autorelease pool(main()里的autorelease)会被销毁,这样这个pool里的每个Object会被release。
可以把autorelease pool理解成一个类似父类与子类的关系,main()创建了父类,每个Runloop自动生成的或者开发者自定义的autorelease pool都会成为该父类的子类。当父类被释放的时候,没有被释放的子类也会被释放,这样所有子类中的对象也会收到release消息。
那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 一个鼠标事件,键盘按下(MAC OSX),或者iphone上的触摸事件,异步http连接下后当接收完数据时,都会是一个新的Runloop。
一般来说,消息循环运行一次是毫秒级甚至微秒级的,因此autorelease的效率仍然是非常高的,确实是一个巧妙的设计。
6、使用有什么要注意的?
1)、NSAutoreleasePool可以创建一个autorelease pool,但该对象本身也需要被释放,如:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
// Code benefitting from a local autorelease pool.
[pool release];
复制代码
在引用计数环境下,使用[pool release]或[pool drain]效果是相同的,drain仅适用于max os高版本,低版本不适用,而release通用,其它并无太大差别。
2)、在ARC下,不能使用上述方式调用autorelease,而应当使用@autoreleasepool,如:
@autoreleasepool {
// Code benefitting from a local autorelease pool.
}
复制代码
3)、尽量避免对大内存使用该方法,如图片。对于这种延迟释放机制,还是尽量少用,最好只用在方法内返回小块内存申请地址值的情况下,且参考和领会OC的一些系统方法,如:[NSString stringWithFormat:]。
4)、不要把大量循环操作放到同一个NSAutoreleasePool之间,这样会造成内存峰值的上升。
7、关于多线程,有什么要注意的?
我还未实际使用到,在官方API翻译出类似如下语句:
1)、对于不同线程,应当创建自己的autorelease pool。如果应用长期存在,应该定期drain和创建新的autorelease pool
下面这句话摘自官方API,大概是说多线程中如果没有使用到cocoa的相关调用,则不需要创建autorelease pool,我一直没有理解透彻
If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.
2)、如果不是使用的NSThread,就不要用aoturelease pool,除非你是多线程模式(multithreading mode) ,可以使用NSThread的isMultiThreaded方法测试你的应用是否是多线程模式
PS:
我把它理解为:新开线程最好实现NSAutoreleasePool(当 我们点击一个App中的一个按钮或者其他可以触碰开启新业务的UI控件,在程序里面就会自动开启一条新线程,当我们不用这个业务的时候,就需要程序帮我们 提前在“程序的主窗口的所有代码编译结束后”之前关闭这条线程以优化线程的使用,一定程度上尽量避免线程开启太多占用CPU严重引起的卡顿问题)(业务逻 辑处理-业务线程优化)
详细可以参考官方API的NSAutoreleasePool Class Reference