iOS---NSAutoreleasePool自动释放原理及详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS---NSAutoreleasePool自动释放原理及详解相关的知识,希望对你有一定的参考价值。

技术分享

当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池 定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。

1. ojc-c 是通过一种"referring counting"(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
2. NSAutoreleasePool 就是用来做引用计数的管理工作的,这个部分后面会详细说到.
3. autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.


NSAutoreleasePool详解:
1. NSAutoreleasePool实际上是个对象引用计数自动处理器。NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一 个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消 息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。


2. 在程序的入口main函数就调用NSAutoreleasePool,这样保证程序中不调用NSAutoreleasePool,但在退出时自动释放。新开线程最好实现NSAutoreleasePool(当我们点击一个App中的一个按钮或者其他可以触碰开启新业务的UI控件,在程序里面就会自动开启一条新线程,当我们不用这个业务的时候,就需要程序帮我们提前在“程序的主窗口的所有代码编译结束后”之前关闭这条线程以优化线程的使用,一定程度上尽量避免线程开启太多占用CPU严重引起的卡顿问题)(业务逻辑处理-业务线程优化)


3. NSAutoreleasePool的管理范围是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];与[pool release];之间的对象


4. NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
当执行[pool autorelease]的时候,系统会进行一次内存释放,把autorelease的对象释放掉,如果没有NSAutoreleasePool , 那这些内存不会释放
注意,对象并不是自动被加入到当前pool中,而是需要对对象发送autorelease消息,这样,对象就被加到当前pool的管理里了。当当前pool接受到drain消息时,它就简单的对它所管理的所有对象发送release消息。
例如


NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* nsstring;
char* cstring = "Hello CString";
nsstring = [NSString stringWithUTF8String:cstring];
[pool release];(这一行代码就是在给pool发送drain消息了)
drain

5. alloc的对象必须显示释放
例如:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* string = [[NSString alloc] init];
[string stringByAppendingString:@"Hello World!"];
[pool release];
[nsstring release];///


6如果使用Automatic Reference Counting(ARC).不能直接使用autorelease pools,而是使用@autoreleasepool{},
@autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的时候也可以使用(autorelease嵌套:)

7 在引用计数环境中,drain和release一样,但是在garbage-collected环境中,使用drain,触发GC(in a garbage-collected environment, triggers garbage collection if the memory allocated since the last collection is greater than the current threshold)。

8.既然设置了ARC,为什么还要使用@autoreleasepool?

ARC 并不是舍弃了 @autoreleasepool,而是在编译阶段帮你插入必要的 retain/release/autorelease 的代码调用。

所以,跟你想象的不一样,ARC 之下依然是延时释放的,依然是依赖于 NSAutoreleasePool,跟非 ARC 模式下手动调用那些函数本质上毫无差别,只是编译器来做会保证引用计数的正确性。

参考:

官方API摘抄翻译:

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,但该对象本身也需要被释放,如:

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
  2. // Code benefitting from a local autorelease pool.
  3. [pool release];
复制代码


       在引用计数环境下,使用[pool release]或[pool drain]效果是相同的,drain仅适用于max os高版本,低版本不适用,而release通用,其它并无太大差别。

2)、在ARC下,不能使用上述方式调用autorelease,而应当使用@autoreleasepool,如:

  1. @autoreleasepool {
  2.    // Code benefitting from a local autorelease pool.
  3. }
复制代码

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方法测试你的应用是否是多线程模式


详细可以参考官方API的NSAutoreleasePool Class Reference

以上是关于iOS---NSAutoreleasePool自动释放原理及详解的主要内容,如果未能解决你的问题,请参考以下文章

自动化测试——何为自动化测试,为何自动化测试

自动化用英语怎么说

SpringBoot的自动装配(一)

Zabbix自动发现与自动注册.

自动变量非自动变量

l4自动驾驶标准是啥?