zoukankan      html  css  js  c++  java
  • 黑幕背后的Autorelease

    我是前言

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

      

        

  • 相关阅读:
    SQL 语法解释器jsqlparser
    《JAVA与模式》之解释器模式
    程序猿应该了解的内容以及程序猿如何强迫自己学习(思考篇)
    强迫自己学习(心态篇),国庆,你准备去哪疯?
    强迫自己学习(实践篇),以及关于写博客的几点建议
    JAVA GC垃圾收集器的分析
    JVM内存模型及内存分配过程
    Extended Traffic LightOJ
    HDU 4616 Game 树形DP
    POJ 3164 Command Network
  • 原文地址:https://www.cnblogs.com/guchengfengyun/p/4525849.html
Copyright © 2011-2022 走看看