软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 75.Autorelease机制及释放时机 -> 正文阅读

[移动开发]75.Autorelease机制及释放时机


Autorelease机制是iOS开发者管理对象内存的好伙伴,MRC中,调用[obj autorelease]来延迟内存的释放是一件简单自然的事;ARC下,我们甚至可以完全不知道Autorelease 系统就能管理好内存。而在这背后,objc和编译器都帮我们做了哪些事呢,一起来探究下Autorelease机制吧。

概述


当向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当自动释放池释放时,其中所有被管理对象都会收到”relrease”的消息, 从而池中的所有对象也就被释放。注意,同一个对象可以被多次调用”autorelease”方法,并可以放到同一个”AutoreleasePool”中。
所以引入这个自动释放池机制,对象的”autorelease”方法代替”relrease”方法可以延长它的生命周期,直接到当前”AutorelreasePool”释放。

iOS通过引用计数管理内存


OC 是通过”referring counting”(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
GC(Garbage Collection) 即垃圾回收, 需要注意的是iOS没有垃圾回收机制的, 只靠引用计数来进行管理内存, 这其实也是 Autorelease 原理的核心. 大家不要混淆这种方法和垃圾回收机制. 不过很多语言还是有自己的垃圾回收机制的, 推荐一篇文章关于垃圾回收(GC)的三种基本方式.

NSAutoreleasePool

如何使用


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 drain];

注意事项


1.NSAutoreleasePool的管理范围是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];与[pool drain];之间的对象
2.在程序的入口main函数就调用NSAutoreleasePool,这样保证程序中不调用NSAutoreleasePool,但在退出时自动释放。新开线程最好实现NSAutoreleasePool
3.NSAutoreleasePool实际上是个对象引用计数自动处理器. NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。
4.如果在Automatic Reference Counting(ARC) 不能直接使用autorelease pools,而是使用@autoreleasepool{}, @autoreleasepool{} 比直接使用NSAutoreleasePool 效率高。但在 MRC 下两者都是适用的.
5.在非 GC的引用计数环境下,drain和release一样,但是在garbage-collected环境中,使用drain。(”release”与”drain”的区别是”drain”在有GC的环境中会引起GC回收操作,”release”反之。但在非GC环境中,两者相同。官方的说法是为了程序的兼容性,应该考虑用”drain”代替”release”。)

Autorelease原理


现在看一段 MRC 下关于单层 autoreleasePool使用的代码:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray * array = <NSArray alloc] init] autorelease];
[pool drain];

AutoreleasePoolPage


ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:
void *context = objc_autoreleasePoolPush();// {}中的代码objc_autoreleasePoolPop(context);

而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。

AutoreleasePoolPage是一个C++实现的类


[img]http://img.blog.csdn.net/20160401152931478
AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。
AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)。
AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。
上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置。
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入。
所以,若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时,内存如下图:
[img]http://img.blog.csdn.net/20160401153017290
上图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。
所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。

释放时刻


每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil),那么这一个page就变成了下面的样子:
[img]http://img.blog.csdn.net/20160401153049041
objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:
1、根据传入的哨兵对象地址找到哨兵对象所处的page。
2、在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置。
3、补充2:从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page,刚才的objc_autoreleasePoolPop执行后,最终变成下面的样子:
[img]http://img.blog.csdn.net/20160401153118994

嵌套的AutoreleasePool


但由于你提到了生成的每个实例可能会比较大。只在循环外嵌套,可能导致在pool释放前,内存里已经有10000个实例存在,造成瞬间占用内存过大的情况。因此,如果你的每个实例仅需要在单次循环过程中用到,那么可以考虑可以在循环内创建pool并释放
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < 10000; i++)
{
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    // ...
    [pool drain];
}
[pool drain];

知道了上面的原理,嵌套的AutoreleasePool就非常简单了,pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已,就像剥洋葱一样,每次一层,互不影响。

Autorelease 释放时机


很多人说, 当程序执行到作用域结束的位置时(当前作用域大括号结束时),自动释放池就会被释放,这个说法是不对的。正确的过程是如何呢?
iOS的运行时是由一个一个runloop组成的,每个runloop都会执行下图的一些步骤:
[img]http://img.blog.csdn.net/20160401155519347
可以看到,每个runloop中都创建一个Autorelease Pool,并在runloop的末尾进行释放,所以,一般情况下,每个接受autorelease消息的对象,都会在下个runloop开始前被释放。也就是说,在一段同步的代码中执行过程中,生成的对象接受autorelease消息后,一般是不会在作用域结束前释放的。
所以严谨的说, 在没有手动添加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。
小实验
__weak id reference = nil;
- (void)viewDidLoad {
    [super viewDidLoad];    NSString *str = [NSString stringWithFormat:@"sunnyxx"];    // str是一个autorelease对象,设置一个weak的引用来观察它
    reference = str;
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];    NSLog(@"%@", reference); // Console: sunnyxx}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];    NSLog(@"%@", reference); // Console: (null)}

由于这个vc在loadView之后便add到了window层级上,所以viewDidLoad和viewWillAppear是在同一个runloop调用的,因此在viewWillAppear中,这个autorelease的变量依然有值。
当然,我们也可以手动干预Autorelease对象的释放时机:
- (void)viewDidLoad
{
    [super viewDidLoad];
    @autoreleasepool {        NSString *str = [NSString stringWithFormat:@"sunnyxx"];
    }    NSLog(@"%@", str); // Console: (null)}

参考资料:Autorelease原理解析
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-02 20:51:48  
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-16 21:17:28
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --