您的位置:首页 > 其它

黑幕背后的Autorelease

2015-05-24 15:03 302 查看
我是前言

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

Autorelease对象什么时候释放?

这个问题拿来做面试题,问过很多人,没有几个能答对的。很多答案都是“当前作用域大括号结束时释放”,显然木有正确理解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的变量依然有值。

- (void)viewDidLoad
{
[super viewDidLoad];
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"sunnyxx"];
}
NSLog(@"%@", str); // Console: (null)}


  

Autorelease原理

AutoreleasePoolPage

ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:

void *context = objc_autoreleasePoolPush();
// {}中的代码objc_autoreleasePoolPop(context);


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

AutoreleasePoolPage是一个C++实现的类



1.AutoreleasePool并没有单独的结构。它是AutoreleasePool通过双向链表的形式连接在一起,对应图片中的parent指针和child指针。

2.AutoreleasePool是与线程一一对应的,图片中的thread指向当前的线程。

3.AutoreleasePoolPage会有2M的大小内存,除开保存自己的实例以外,其它的用来保存autorelease对象。

4.图片中的id *next作为游标指针指向下一个将要add进来的autorelease对象。

5.一个autorelease对象内存空间被占满的时候,会新建下一个autoreleasePage对象,并用链表连接起来,新创建的autorelease对象会被添加到新的autoreleasePage里面。

所以如果当前只有一个autoreleasePool对象,情况如下所示



如上图所示,再插入一个autorelease对象,就要栈满了,会新创建一个page对象,新的autorelease对象会被放在栈底。

所以向一个新的对象发送autorelease对象就是向page里面的插入到next指针位置

释放时刻

每当进行一次objc_AutoreleasePoolPush调用的时候,runtime会添加一个哨兵对象,为nil







objc_autoreleasePoolPush对象的返回值就是这个哨兵对象的地址,被objc_autoreleasePop(哨兵对象)作为入参,于是:

1.根据哨兵对象的地址找到哨兵对象所在的page

2.在当前page中,将晚于哨兵对象都发送一次release操作,并向前移动next到正确位置。

3.从最新加入的对象开始,一直向前清理,可以跨越多个page

刚才的objc_autoreleasePoolPop操作后,变成:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: