Objective-C 对象的内存管理-自动释放池

autorelease

    说到 Objective-C 内存管理,就不能不提到 autorelease。顾名思义,autorelease 就是自动释放。自动释放池是OC中的一种内存自动回收机制,它可以控制 autoreleasePool中的变量release 的时机,简单来说,就是当创建一个对象,在正常情况下,变量会在超出其作用域的时立即 release。如果将对象加入到了自动释放池中,这个对象并不会立即释放,会等到runloop 休眠/超出 autoreleasepool 作用域 {} 之后才会被释放。
    autorelease 的使用方法如下:

@autoreleasepool {
    #code
}

__AtAutoreleasePool

     autorelease 是怎样实现的呢? 通过命令 xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m 编译文件生成 main.cpp 文件。源码如下:

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

int main(int argc, const char * argv[]) {
   { 
        //是一个结构体
         __AtAutoreleasePool __autoreleasepool; 
    }
    return 0;
}

     通过编译器模拟代码可以看到, autoreleasepool 其实就是 一个__AtAutoreleasePool 的结构体,有构造函数和析构函数,结构体定义的对象在作用域结束后,会自动调用析构函数。autoreleasepool 在加入要释放的对象时,底层调用的是objc_autoreleasePoolPush 方法,在调用析构函数释放时,内部的实现是调用 objc_autoreleasePoolPop 方法

AutoreleasePoolPage

     在 objc 源码中,搜索 objc_autoreleasePoolPushobjc_autoreleasePoolPop 源码如下:


void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}


     可以看到两个函数都是通过 AutoreleasePoolPage 调用 pushpop 函数,查看 AutoreleasePoolPage 源码如下:

//************宏定义************
#define PAGE_MIN_SIZE           PAGE_SIZE
#define PAGE_SIZE               I386_PGBYTES
#define I386_PGBYTES            4096            /* bytes per 80386 page */

//************类定义************
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
 friend struct thread_data_t;

public:
 static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
  PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
  PAGE_MIN_SIZE;  // size and alignment, power of 2 4096
#endif
    
private:
 static pthread_key_t const key = AUTORELEASE_POOL_KEY;
 static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
 static size_t const COUNT = SIZE / sizeof(id);

    // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is 
    // pushed and it has never contained any objects. This saves memory 
    // when the top level (i.e. libdispatch) pushes and pops pools but 
    // never uses them.
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)

#   define POOL_BOUNDARY nil

    // SIZE-sizeof(*this) bytes of contents follow
   
    static void * operator new(size_t size) {
        return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
    }
    static void operator delete(void * p) {
        return free(p);
    }

    inline void protect() {
#if PROTECT_AUTORELEASEPOOL
        mprotect(this, SIZE, PROT_READ);
        check();
#endif
    }

    inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
        check();
        mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
    }
 //构造函数
 AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
  AutoreleasePoolPageData(begin(),
        objc_thread_self(),
        newParent,
        newParent ? 1+newParent->depth : 0,
        newParent ? newParent->hiwat : 0)
    { 
        if (parent) {
            parent->check();
            ASSERT(!parent->child);
            parent->unprotect();
            parent->child = this;
            parent->protect();
        }
        protect();
    }
    //析构函数
    ~AutoreleasePoolPage() 
    {
        check();
        unprotect();
        ASSERT(empty());

        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        ASSERT(!child);
    }

    template<typename Fn>
    void
    busted(Fn log) const
    {
        magic_t right;
        log("autorelease pool page %p corrupted\n"
             "  magic     0x%08x 0x%08x 0x%08x 0x%08x\n"
             "  should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
             "  pthread   %p\n"
             "  should be %p\n", 
             this, 
             magic.m[0], magic.m[1], magic.m[2], magic.m[3], 
             right.m[0], right.m[1], right.m[2], right.m[3], 
             this->thread, objc_thread_self());
    }

    __attribute__((noinline, cold, noreturn))
    void
    busted_die() const
    {
        busted(_objc_fatal);
        __builtin_unreachable();
    }

    inline void
    check(bool die = true) const
    {
        if (!magic.check() || thread != objc_thread_self()) {
            if (die) {
                busted_die();
            } else {
                busted(_objc_inform);
            }
        }
    }

    inline void
    fastcheck() const
    {
#if CHECK_AUTORELEASEPOOL
        check();
#else
        if (! magic.fastcheck()) {
            busted_die();
        }
#endif
    }

    //开始存储的位置
    id * begin() {
        return (id *) ((uint8_t *)this+sizeof(*this));
    }
    //存储结束的位置
    id * end() {
        return (id *) ((uint8_t *)this+SIZE);
    }
   //是否空页
    bool empty() {
        return next == begin();
    }
    //是否满页
    bool full() { 
        return next == end();
    }
   //页的存储是否少于一半
    bool lessThanHalfFull() {
        return (next - begin() < (end() - begin()) / 2);
    }
   // 添加对象
    id *add(id obj)
    {
        ASSERT(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }
   //释放所有对象
    void releaseAll() 
    {
        releaseUntil(begin());
    }
    //释放到stop位置之前的所有对象
    void releaseUntil(id *stop) 
    {
        //...
    }
    //结束
    void kill() 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        AutoreleasePoolPage *page = this;
        while (page->child) page = page->child;

        AutoreleasePoolPage *deathptr;
        do {
            deathptr = page;
            page = page->parent;
            if (page) {
                page->unprotect();
                page->child = nil;
                page->protect();
            }
            delete deathptr;
        } while (deathptr != this);
    }
    //释放本地线程存储空间
    static void tls_dealloc(void *p) 
    {
        if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
            // No objects or pool pages to clean up here.
            return;
        }

        // reinstate TLS value while we work
        setHotPage((AutoreleasePoolPage *)p);

        if (AutoreleasePoolPage *page = coldPage()) {
            if (!page->empty()) objc_autoreleasePoolPop(page->begin());  // pop all of the pools
            if (slowpath(DebugMissingPools || DebugPoolAllocation)) {
                // pop() killed the pages already
            } else {
                page->kill();  // free all of the pages
            }
        }
        
        // clear TLS value so TLS destruction doesn't loop
        setHotPage(nil);
    }
    //获取AutoreleasePoolPage
    static AutoreleasePoolPage *pageForPointer(const void *p) 
    {
        return pageForPointer((uintptr_t)p);
    }

    static AutoreleasePoolPage *pageForPointer(uintptr_t p) 
    {
        AutoreleasePoolPage *result;
        uintptr_t offset = p % SIZE;

        ASSERT(offset >= sizeof(AutoreleasePoolPage));

        result = (AutoreleasePoolPage *)(p - offset);
        result->fastcheck();

        return result;
    }

    //是否有空池占位符
    static inline bool haveEmptyPoolPlaceholder()
    {
        id *tls = (id *)tls_get_direct(key);
        return (tls == EMPTY_POOL_PLACEHOLDER);
    }
    //设置空池占位符
    static inline id* setEmptyPoolPlaceholder()
    {
        ASSERT(tls_get_direct(key) == nil);
        tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
        return EMPTY_POOL_PLACEHOLDER;
    }
    //获取当前操作页
    static inline AutoreleasePoolPage *hotPage() 
    {
        //获取当前页
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);
        //如果是一个空池,则返回nil,否则,返回当前线程的自动释放池
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();
        return result;
    }
     //设置当前操作页
    static inline void setHotPage(AutoreleasePoolPage *page) 
    {
        if (page) page->fastcheck();
        tls_set_direct(key, (void *)page);
    }
    //获取coldPage
    static inline AutoreleasePoolPage *coldPage() 
    {
        AutoreleasePoolPage *result = hotPage();
        if (result) {
            while (result->parent) {
                result = result->parent;
                result->fastcheck();
            }
        }
        return result;
    }

    //快速 autorelease
    static inline id *autoreleaseFast(id obj)
    {
        //...
    }
    //添加自动释放对象,当页满的时候调用这个方法
    static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        //...
    } 
    //添加自动释放对象,当没页的时候使用这个方法
    static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        //...
    }

    //创建新页
    static __attribute__((noinline))
    id *autoreleaseNewPage(id obj)
    {
        //...
    }

public:
    //添加需要自动释放的对象
    static inline id autorelease(id obj)
    {
        //...
    }

    //入栈
    static inline void *push() 
    {
        //...
    }
     //异常
    __attribute__((noinline, cold))
    static void badPop(void *token)
    {
        // Error. For bincompat purposes this is not 
        // fatal in executables built with old SDKs.

        if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
            // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
            _objc_fatal
                ("Invalid or prematurely-freed autorelease pool %p.", token);
        }

        // Old SDK. Bad pop is warned once.
        static bool complained = false;
        if (!complained) {
            complained = true;
            _objc_inform_now_and_on_crash
                ("Invalid or prematurely-freed autorelease pool %p. "
                 "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
                 "Proceeding anyway because the app is old "
                 "(SDK version " SDK_FORMAT "). Memory errors are likely.",
                     token, FORMAT_SDK(sdkVersion()));
        }
        objc_autoreleasePoolInvalid(token);
    }
    //出栈 Page
    template<bool allowDebug>
    static void
    popPage(void *token, AutoreleasePoolPage *page, id *stop)
    {
        if (allowDebug && PrintPoolHiwat) printHiwat();

        page->releaseUntil(stop);

        // memory: delete empty children
        if (allowDebug && DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (allowDebug && DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            // special case: delete everything for pop(top)
            // when debugging missing autorelease pools
            page->kill();
            setHotPage(nil);
        } else if (page->child) {
            // hysteresis: keep one empty child if page is more than half full
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

    __attribute__((noinline, cold))
    static void
    popPageDebug(void *token, AutoreleasePoolPage *page, id *stop)
    {
        popPage<true>(token, page, stop);
    }
    //出栈
    static inline void
    pop(void *token)
    {
        //...
    }

    static void init()
    {
        int r __unused = pthread_key_init_np(AutoreleasePoolPage::key, 
                                             AutoreleasePoolPage::tls_dealloc);
        ASSERT(r == 0);
    }
    //打印
    __attribute__((noinline, cold))
    void print()
    {
        _objc_inform("[%p]  ................  PAGE %s %s %s", this, 
                     full() ? "(full)" : "", 
                     this == hotPage() ? "(hot)" : "", 
                     this == coldPage() ? "(cold)" : "");
        check(false);
        for (id *p = begin(); p < next; p++) {
            if (*p == POOL_BOUNDARY) {
                _objc_inform("[%p]  ################  POOL %p", p, p);
            } else {
                _objc_inform("[%p]  %#16lx  %s", 
                             p, (unsigned long)*p, object_getClassName(*p));
            }
        }
    }
    //打印
    __attribute__((noinline, cold))
    static void printAll()
    {
        _objc_inform("##############");
        _objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());

        AutoreleasePoolPage *page;
        ptrdiff_t objects = 0;
        for (page = coldPage(); page; page = page->child) {
            objects += page->next - page->begin();
        }
        _objc_inform("%llu releases pending.", (unsigned long long)objects);

        if (haveEmptyPoolPlaceholder()) {
            _objc_inform("[%p]  ................  PAGE (placeholder)", 
                         EMPTY_POOL_PLACEHOLDER);
            _objc_inform("[%p]  ################  POOL (placeholder)", 
                         EMPTY_POOL_PLACEHOLDER);
        }
        else {
            for (page = coldPage(); page; page = page->child) {
                page->print();
            }
        }

        _objc_inform("##############");
    }

    __attribute__((noinline, cold))
    static void printHiwat()
    {
        // Check and propagate high water mark
        // Ignore high water marks under 256 to suppress noise.
        AutoreleasePoolPage *p = hotPage();
        uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
        if (mark > p->hiwat  &&  mark > 256) {
            for( ; p; p = p->parent) {
                p->unprotect();
                p->hiwat = mark;
                p->protect();
            }

            _objc_inform("POOL HIGHWATER: new high water mark of %u "
                         "pending releases for thread %p:",
                         mark, objc_thread_self());

            void *stack[128];
            int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
            char **sym = backtrace_symbols(stack, count);
            for (int i = 0; i < count; i++) {
                _objc_inform("POOL HIGHWATER:     %s", sym[i]);
            }
            free(sym);
        }
    }

#undef POOL_BOUNDARY
};

    从上面代码可以得知,AutoreleasePoolPage 继承于 AutoreleasePoolPageData , 是一个 page,其容量大小为 4096 字节。AutoreleasePoolPageData 结构体的内存大小为 56 字节,并且通过 parentchild 可以查找父子结点。AutoreleasePoolPageData 源码如下:

struct AutoreleasePoolPageData
{
    //用来校验AutoreleasePoolPage的结构是否完整
    magic_t const magic;//16个字节
    //指向最新添加的autoreleased对象的下一个位置,初始化时指向begin()
    __unsafe_unretained id *next;//8字节
    //指向当前线程
    pthread_t const thread;//8字节
    //指向父节点,第一个结点的parent值为nil
    AutoreleasePoolPage * const parent;//8字节
    //指向子节点,最后一个结点的child值为nil
    AutoreleasePoolPage *child;//8字节
    //表示深度,从0开始,往后递增1
    uint32_t const depth;//4字节
    //表示high water mark 最大入栈数量标记
    uint32_t hiwat;//4字节

    //初始化
    AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
        : magic(), next(_next), thread(_thread),
          parent(_parent), child(nil),
          depth(_depth), hiwat(_hiwat)
    {
    }
};

    因此,autoreleasepool 其本质是一个结构体对象,一个自动释放池对象就是页,页内是栈结构存储,页与页之间是双向链表,符合先进后出的原则。页的栈底是一个 56 字节大小的空占位符,一页总大小为 4096 字节。只有第一页有哨兵对象,最多存储504 个对象,从第二页开始最多存储 505 个对象。autoreleasepool 在加入要释放的对象时,底层调用的是 objc_autoreleasePoolPush方法。autoreleasepool 在调用析构函数释放时,内部的实现是调用 objc_autoreleasePoolPop方法。

autorelease 实现

push()

objc_autoreleasePoolPush 会调用 AutoreleasePoolPagepush 函数进入压栈流程:

  • DebugPoolAllocationtrue 时,每一个自动释放池都需要从新的 Pool Page 开始,调用 autoreleaseNewPage 函数;
  • DebugPoolAllocationfalse 时,进行压栈哨兵对象 POOL_BOUNDARY,调用 autoreleaseFast
//入栈
    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;
    }

autoreleaseNewPage()

     autoreleaseNewPage 函数其实就是一个创建页并压栈对象的函数,调用 hotPage() 函数获取当前页后,判断 page 是否存在:

  • 如果 page 存在,调用 autoreleaseFullPage 函数压栈对象
  • 如果 page 不存在,调用 autoreleaseNoPage 函数创建页
  //创建新页
    static __attribute__((noinline))
    id *autoreleaseNewPage(id obj)
    {
        //获取当前页
        AutoreleasePoolPage *page = hotPage();
        //如果存在,则压栈对象
        if (page) return autoreleaseFullPage(obj, page);
        //如果不存在,则创建页
        else return autoreleaseNoPage(obj);
    }

autoreleaseFast()

     autoreleaseFast 函数看起来更像一个入口函数,获取当前页 page,通过下面流程可以发现 autoreleaseFast 和 autoreleaseNewPage 函数类似,都会调用 autoreleaseFullPageautoreleaseNoPage 函数,差别仅在于调用 add 函数:

  • 如果当前 page 存在,并且页未填充满(!full),直接调用 add 函数添加当前对象
  • 如果当前 page 存在, 并且页满(full),调用 autoreleaseFullPage 函数
  • 如果当前 page 不存在,调用 autoreleaseNoPage 函数
 //快速 autorelease
    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

autoreleaseFullPage()

     autoreleaseFullPage 函数用于处理页满的情况,会根据链表子结点 child 寻找到最后一个结点,调用 AutoreleasePoolPage 构造函数后,将对象添加到新的 page

 //添加自动释放对象,当页满的时候调用这个方法
    static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        // The hot page is full. 
        // Step to the next non-full page, adding a new page if necessary.
        // Then add the object to that page.
        ASSERT(page == hotPage());
        ASSERT(page->full()  ||  DebugPoolAllocation);

        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());

        setHotPage(page);
        return page->add(obj);
    } 

autoreleaseNoPage()

     autoreleaseNoPage 用于创建第一页,调用 page 构造函数,判断当前 obj 是否为哨兵对象 POOL_BOUNDARY,并进行压栈对象

 //添加自动释放对象,当没页的时候使用这个方法
    static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        // "No page" could mean no pool has been pushed
        // or an empty placeholder pool has been pushed and has no contents yet
        ASSERT(!hotPage());

        bool pushExtraBoundary = false;
         //判断是否是空占位符,如果是,则压栈哨兵标识符置为YES
        if (haveEmptyPoolPlaceholder()) {
            // We are pushing a second pool over the empty placeholder pool
            // or pushing the first object into the empty placeholder pool.
            // Before doing that, push a pool boundary on behalf of the pool 
            // that is currently represented by the empty placeholder.
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            // We are pushing an object with no pool in place, 
            // and no-pool debugging was requested by environment.
            //如果对象不是哨兵对象,且没有Pool,则报错
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         objc_thread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            // We are pushing a pool with no pool in place,
            // and alloc-per-pool debugging was not requested.
            // Install and return the empty pool placeholder.


            //如果对象是哨兵对象,且没有申请自动释放池内存,则设置一个空占位符存储在tls中,其目的是为了节省内存
            return setEmptyPoolPlaceholder();
        }

        // We are pushing an object or a non-placeholder'd pool.

        // Install the first page.
         //初始化第一页
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        //设置page为当前聚焦页
        setHotPage(page);
        
        // Push a boundary on behalf of the previously-placeholder'd pool.
        if (pushExtraBoundary) {
             //压栈哨兵的标识符为YES,则压栈哨兵对象
            page->add(POOL_BOUNDARY);
        }
        
        // Push the requested object or pool.
          // Push the requested object or pool.
          //压栈对象
        return page->add(obj);
    }

     总结,push 流程就是初始化 page,保证当前存在 page,并且 page 中有哨兵对象 POOL_BOUNDARY,防止之后的出栈越界,流程如下图:

autorelease()

     当 objc_autoreleasePoolPush() 函数 也就是 AutoreleasePoolPage::push 流程执行完后, 就会进入到自动释放池的作用域中,对每个创建的变量执行 autorelease 方法,通过 objc 源码查看,可以发现 autorelease方法,最终调用 AutoreleasePoolPage::autorelease 函数,从而执行 autoreleaseFast 函数进行压栈对象。源码如下:

- (id)autorelease {
    return _objc_rootAutorelease(self);
}
_objc_rootAutorelease(id obj)
{
    ASSERT(obj);
    return obj->rootAutorelease();
}
inline id 
objc_object::rootAutorelease()
{
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;

    return rootAutorelease2();
}

__attribute__((noinline,used))
id 
objc_object::rootAutorelease2()
{
    ASSERT(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}

 //添加需要自动释放的对象
    static inline id autorelease(id obj)
    {
        ASSERT(obj);
        ASSERT(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);
        ASSERT(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }

     总结,整个流程 autorelease -> _objc_rootAutorelease -> rootAutorelease -> rootAutorelease2 -> AutoreleasePoolPage::autorelease -> autoreleaseFast

pop()

    当离开自动释放池作用域时,会调用 AutoreleasePoolPage::pop 函数,获取 page 、找到 stop 指针为 page->begin(),调用 popPage 函数,源码如下:

 //出栈
    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.
             //如果当前页存在,则将当前页设置为coldPage,token设置为coldPage的开始位置
            page = coldPage();
            token = page->begin();
        } else {
            //获取token所在的页
            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);
    }

popPage()

     执行 popPage 流程,处理 page,流程如下:

  • 调用 releaseUntil 方法释放对象

  • 调用 kill 方法 释放 page

//出栈 Page
    template<bool allowDebug>
    static void
    popPage(void *token, AutoreleasePoolPage *page, id *stop)
    {
        if (allowDebug && PrintPoolHiwat) printHiwat();

        page->releaseUntil(stop);

        // memory: delete empty children
        if (allowDebug && DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (allowDebug && DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            // special case: delete everything for pop(top)
            // when debugging missing autorelease pools
            page->kill();
            setHotPage(nil);
        } else if (page->child) {
            // hysteresis: keep one empty child if page is more than half full
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

releaseUntil()

releaseUntil 递归 next 指针出栈所有 obj 并调用 objc_release 对其引用计数进行 -1 操作,直到 stop 指针位置

 //释放到stop位置之前的所有对象
    void releaseUntil(id *stop) 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }

            page->unprotect();
            id obj = *--page->next;
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
            page->protect();

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);
            }
        }
        setHotPage(this);

#if DEBUG
        // we expect any children to be completely empty
        for (AutoreleasePoolPage *page = child; page; page = page->child) {
            ASSERT(page->empty());
        }
#endif
    }

    总结,当离开自动释放池作用域时,遍历 AutoRelesaePoolPage 中的对象进行出栈并对其调用 objc_relesae,清空 Page 后,对 Page 进行 kill。至此,整个 autoreleasePool 流程完成。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容