autoreleasepool - ShenYj/ShenYj.github.io GitHub Wiki

@autoreleasepool

在 ARC 模式下,@autoreleasepool 已经很少被使用了

其对应的是一个 C++ 结构体对象 __AtAutoreleasePool, 可通过编译成 C++ 代码查阅

struct __AtAutoreleasePool {

  // 构造函数 
  __AtAutoreleasePool() { atautoreleasepoolobj = objc_autoreleasePoolPush(); }
  // 析构函数
  ~__AtAutoreleasePool() { objc_autoreleasePoolPop(atautoreleasepoolobj); }

  void * atautoreleasepoolobj;
};

在程序入口 main 函数中默认包含一个 @autoreleasepool

/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
  • 最终会创建一个 __AtAutoreleasePool 实例

  • 并在创建的时候自动调用 objc_autoreleasePoolPush() 函数

    • 内部调用 AutoreleasePoolPage::push()

      static inline void *push() 
          {
              id *dest;
              if (slowpath(DebugPoolAllocation)) {
                  // Each autorelease pool starts on a new pool page.
                  dest = autoreleaseNewPage(POOL_BOUNDARY);
              } else {
                  dest = autoreleaseFast(POOL_BOUNDARY);
              }
              ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
              return dest;
          }
  • 在销毁的时候自动调用 objc_autoreleasePoolPop(atautoreleasepoolobj) 函数

    • 内部调用 AutoreleasePoolPage::pop(ctxt)

      static inline void
          pop(void *token)
          {
              AutoreleasePoolPage *page;
              id *stop;
              if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
                  // Popping the top-level placeholder pool.
                  page = hotPage();
                  if (!page) {
                      // Pool was never used. Clear the placeholder.
                      return setHotPage(nil);
                  }
                  // Pool was used. Pop its contents normally.
                  // Pool pages remain allocated for re-use as usual.
                  page = coldPage();
                  token = page->begin();
              } else {
                  page = pageForPointer(token);
              }
      
              stop = (id *)token;
              if (*stop != POOL_BOUNDARY) {
                  if (stop == page->begin()  &&  !page->parent) {
                      // Start of coldest page may correctly not be POOL_BOUNDARY:
                      // 1. top-level pool is popped, leaving the cold page in place
                      // 2. an object is autoreleased with no pool
                  } else {
                      // Error. For bincompat purposes this is not 
                      // fatal in executables built with old SDKs.
                      return badPop(token);
                  }
              }
      
              if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
                  return popPageDebug(token, page, stop);
              }
      
              return popPage<false>(token, page, stop);
          }

可以看出来,自动释放池的主要结构就是 AutoreleasePoolPage

  • AutoreleasePoolPage

    class AutoreleasePoolPage : private AutoreleasePoolPageData
    {
        magic_t const magic;
        __unsafe_unretained id *next;       // 指向存放下一 个 autorelease 对象的地址
        pthread_t const thread;
        AutoreleasePoolPage * const parent; // 指向上一个 `AutoreleasePoolPage`
        AutoreleasePoolPage *child;         // 指向下一个 `AutoreleasePoolPage`
        uint32_t const depth;
        uint32_t hiwat;
    };

    AutoreleasePoolPage 主要是提供了大量的静态函数,成员都是继承自父类 AutoreleasePoolPageData

  • 每个 AutoreleasePoolPage 会占用 4096字节(4K) 大小,除了用来存放它的内部成员变量(56字节),剩下的空间(4040字节)用来存放 autorelease 对象的地址

  • 所有的 AutoreleasePoolPage 对象通过双向链表的形式连接在一起

  • 初始化一个 AutoreleasePoolPage 调用 push 函数时,首先会将一个 POOL_BOUNDARY 入栈,返回其存放的内存地址, 也就是在成员变量后插入一个 nil,返回存放的内存地址给 atautoreleasepoolobj

    define POOL_BOUNDARY nil
  • 接下来再向 autoreleasepool 中存放对象时,就会按顺序存储对象的地址

  • 如果当前 AutoreleasePoolPage 被存满,会再次新建一个 AutoreleasePoolPage 继续存储

  • autoreleasepool 大括号结束需要释放内存时,会调用 objc_autoreleasePoolPop 函数并将 atautoreleasepoolobj 作为参数传过去, atautoreleasepoolobj 存放的是 POOL_BOUNDARY 地址, 从后向前执行 pop 移除对象直到 POOL_BOUNDARY

⚠️ **GitHub.com Fallback** ⚠️