码迷,mamicode.com
首页 > 其他好文 > 详细

黑幕背后的Autorelease

时间:2015-05-24 15:26:11      阅读:495      评论:0      收藏:0      [点我收藏+]

标签:

我是前言

    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操作后,变成:

  技术分享

 

    

黑幕背后的Autorelease

标签:

原文地址:http://www.cnblogs.com/guchengfengyun/p/4525849.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!