zoukankan      html  css  js  c++  java
  • Autorelease Pool-自动释放池

        Autorelease Pool是Objective-C中的内存管理方式之一,它与线程和NSAutorelease类有关。每一个线程都拥有自己的Autorelease Pool栈,这个栈底层是由双向链表构成,而双向链表由类AutoreleasePoolPage表示。AutoreleasePoolPage是一个C++类,这个类的结构如下(只给出了部分相关成员变量):

    class AutoreleasePoolPage  {
        static pthread_key_t const key = AUTORELEASE_POOL_KEY;  //和线程相关的key值
        static size_t const SIZE = 
    #if PROTECT_AUTORELEASEPOOL
            PAGE_MAX_SIZE;  // must be multiple of vm page size
    #else
            PAGE_MAX_SIZE;  // size and alignment, power of 2
    #endif
        static size_t const COUNT = SIZE / sizeof(id); //每一个page的大小
        id *next; //加入当前page中的Objective-C对象地址会传给next
        pthread_t const thread;  //线程的引用
        AutoreleasePoolPage * const parent;  //指向父节点
        AutoreleasePoolPage *child;  //指向子节点
     };

     线程与Autorelease Pool栈的关系如下图所示:

    上图中,加颜色部分表示加入到自动释放池中的对象,而next总是指向下一个可用位置。当前线程总是可以通过AutoreleasePoolPage中的key属性获取到位于栈最顶端(图中就是最右边)的AutoreleasePoolPage对象,这个对象被称为hot page。

        需要注意的是,并不是一个AutoreleasePoolPage对象就代表一个自动释放池,一个自动释放池可以横跨多个AutoreleasePage对象。自动释放池之间是通过哨兵(Sentinel)来分隔的。那么什么是哨兵,NSAutoreleasePool对象和AutoreleasePoolPage对象之间又有什么关系呢?

        当我们使用如下方法创建NSAutoreleasePool对象时:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSAutoreleasePool对象的init方法实际上会调用AutoreleasePoolPage类的static push方法:

      static inline void *push() 
        {
            id *dest = autoreleaseFast(POOL_SENTINEL);
            assert(*dest == POOL_SENTINEL);
            return dest;
        }

    AutoreleasePoolPage::push方法中的autoreleaseFast方法传入的就是一个哨兵,其实就是一个nil值。而AutoreleasePoolPage::autoreleaseFast方法会做3件事:

    1 通过AutoreleasePoolPage的key属性获取当前线程对应的hot page,如果hot page存在并且未满,那么就向当前page中加入哨兵;

    2 如果当前hot page已满,那么就创建一个新的AutoreleasePoolPage对象,加入到链表最顶端使成为hot page,并将哨兵加入到新的hot page中;

    3 如果hot page根本不存在,那么也会创建一个新的AutoreleasePoolPage对象,使之成为hot page,并将哨兵加入到新hot page中。

    当加入了一个哨兵之后,就代表一个新自动释放池开始。AutoreleasePoolPage::push方法会将这个哨兵地址返回回去,而NSAutoreleasePool对象会持有这个哨兵地址:

    @interface NSAutoreleasePool : NSObject {
    @private
        void    *_token;   //保存哨兵地址
        void    *_reserved3;
        void    *_reserved2;
        void    *_reserved;
    }

    NSAutoreleasePool对象当中的_token成员,就是用来保存哨兵地址的。因此,线程,AutoreleaesPoolPage对象,NSAutoreleasePool对象之间的关系可以表示成:

    上图中有两个哨兵,因此有两个自动释放池,第一个自动释放池由NSAutoreleasePool对象_1表示,范围从第一个哨兵开始到第二个哨兵下面,第二个自动释放池由NSAutoreleasePool对象_2表示,范围从第二个哨兵开始直到结束。这也是自动释放池嵌套时的情形。

        当自动释放池释放,即调用NSAutoreleasePool对象的release或者drain时会发生什么情况呢?

        当调用NSAutoreleasePool对象的release或者drain方法时,它们将NSAutoreleasePool对象保存的哨兵地址传递会给AutoreleasePoolPoage的static pop方法,这个AutoreleasePoolPage::pop方法主要做了如下2件事:

    1 通过相应算法,根据哨兵地址找到哨兵所在的AutoreleasePoolPage对象;

    2 从hot page开始,直到哨兵对象为止,依次弹出加入到里面的Objective-C对象,并且向Objective-C对象发送release消息

    假设释放了上面的NSAutoreleasePool对象_2,那么结果如下图所示:

    从Apple关于NSAutoreleasePool的官方文档我们可以知道,NSAutoreleasePool对象无法被retain,因此,当AutoreleasePoolPage::pop方法调用完成之后,NSAutoreleasePool对象的release或者drain方法就会释放掉这个NSAutoreleasePool对象本身。到这里为止,我们又可以向上一个自动释放池当中添加新的Objective-C对象了。

  • 相关阅读:
    Java 中Timer和TimerTask 定时器和定时任务使用的例子
    PowerDesigner逆向工程导入MYSQL数据库总结
    Powerdesigner 连接mysql 在指定的DSN中,驱动程序和应用程序之间的体系结构不匹配 SQLSTATE = IM014
    关于web.xml中的<welcome-file-list>
    SQL查询四舍五入 解决方法
    HTML页面跳转的5种方法
    easyUI datagrid 列宽自适应(简单 图解)(转)
    navicat for mysql只导出数据表结构(转)
    每一位想有所成就的程序员都必须知道的15件事(走不一样的路,要去做,实践实践再实践,推销自己,关注市场)good
    2017除夕夜的感悟:学习工作不分家,工作生活不分家,读书用兵不分家
  • 原文地址:https://www.cnblogs.com/chaoguo1234/p/4925857.html
Copyright © 2011-2022 走看看