OC_自动释放池autoreleasepool

序言:翻阅资料,学习,探究,总结,借鉴,谢谢探路者,我只是个搬运工。
参考、转发资料:
http://draveness.me/autoreleasepool/

1. 自动释放池autoreleasepool介绍

自动释放池储于内存中的栈中遵循"先进后出"原则。autoreleasepool就是一个容器,盛放添加到其中的变量。存在的两种形式,一种是在项目创建时中的main.m文件就有一个,即程序运行开始就帮我们创建了最外层,也是最大的一个自动释放池。另一种就是根据我们自己的需求自己创建了。而其作用就是自动释放池释放的时候帮助我们释放一些存储在释放池中的内存变量,降低整个程序在运行过程中内存消耗过大。

2. autoreleasepool内部结构的分析。

  1. autoreleasepool 就是一个结构体的变量。
    autoreleasepool是 __AtAutoreleasePool 结构体的一个变量。
struct __AtAutoreleasePool {  
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

这个结构体会在初始化时调用 objc_autoreleasePoolPush() 方法,会在析构时调用 objc_autoreleasePoolPop 方法。
这表明,我们的 main 函数在实际工作时其实是这样的:

int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();
        
        // do whatever you want
        
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}
  1. 分析方法 objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 的实现
void *objc_autoreleasePoolPush(void) {
      return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
      AutoreleasePoolPage::pop(ctxt);
}
  1. AutoreleasePoolPage 的结构
class AutoreleasePoolPage {  
    magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
};
- magic 用于对当前 AutoreleasePoolPage 完整性的校验
- thread 保存了当前页所在的线程
  1. 自动释放池中的 AutoreleasePoolPage 是以双向链表的形式连接起来的:
    parent 和 child 就是用来构造双向链表的指针。

    OC_自动释放池autoreleasepool_第1张图片
    AutoreleasePoolPage的双向链表.png

  2. 自动释放池中的栈
    如果我们的一个 AutoreleasePoolPage 被初始化在内存的 0x100816000 ~ 0x100817000 中,它在内存中的结构如下:


    OC_自动释放池autoreleasepool_第2张图片
    栈中内存存储.png

    从0x100816038 ~ 0x100817000 都是用来存储加入到自动释放池中的对象。begin() 和 end() 这两个类的实例方法帮助我们快速获取 0x100816038 ~ 0x100817000 这一范围的边界地址。

next 指向了下一个为空的内存地址,如果 next 指向的地址加入一个 object,它就会如下图所示移动到下一个为空的内存地址中:


OC_自动释放池autoreleasepool_第3张图片
自动释放池中变量的存储过程.png
  1. POOL_SENTINEL(哨兵对象)这是个非常重要的变量
    POOL_SENTINEL 只是 nil 的别名,它是用来记录位置的。记录每个新的自动释放池存储位置的开始。模块化的划分每个自动释放池中存储的变量。
    回顾最开始的知识点
int main(int argc, const char * argv[]) {  
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();

        // do whatever you want

        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

上面的 atautoreleasepoolobj 就是一个 POOL_SENTINEL。告诉我们记录这个自动释放池的开始位置。

当方法 objc_autoreleasePoolPop 调用时,就会向自动释放池中的对象发送 release 消息,直到第一个 POOL_SENTINEL:


OC_自动释放池autoreleasepool_第4张图片
自动释放池的释放过程.png
  1. 释放自动释放池
  • 使用 pageForPointer 获取当前所在的 AutoreleasePoolPage
  • 调用 releaseUntil 方法释放栈中的对象,直到当前页数中的最后一个
  • 调用 child 的 kill 方法,将当前页面以及子页面全部删除

3. 总结

  • 自动释放池是存储在栈中,那加入到自动释放池的变量也在栈中存储一份指向变量的指针地址,变量在自动释放池当中的存储是从低地址指向高地址。当前自动释放池释放了,那么这些变量才会从内存当中释放。
  • 在自动释放池的结构中POOL_SENTINEL(哨兵对象)记录你的位置,区分每个自动释放池的开始位置,以便于区分多层次释放的某一个自动释放池。
  • 当方法 objc_autoreleasePoolPop 调用时,就会向自动释放池中的对象发送 release 消息,直到第一个 POOL_SENTINEL,此时最里层的自动释放池将得到释放,遵循入栈的形式,先进后出。

你可能感兴趣的:(OC_自动释放池autoreleasepool)