美文网首页
iOS 自动释放池autoreleasepool(二)

iOS 自动释放池autoreleasepool(二)

作者: 尤先森 | 来源:发表于2019-04-29 11:37 被阅读0次

    上一篇文章介绍了autoreleasepool的AutoreleasepoolPage,这篇文章我们就来主要讲讲AutoreleasepoolPage::PushAutoreleasepoolPage::Pop 还有一个autorelease

    AutoreleasepoolPage::Push

    static inline void *push() 
        {
            id *dest;
            if (DebugPoolAllocation) {
                // Each autorelease pool starts on a new pool page.
                // 每个autoreleasepool从一个新的page开始
                dest = autoreleaseNewPage(POOL_BOUNDARY);
            } else {
                // 已经有线程的页
                dest = autoreleaseFast(POOL_BOUNDARY);
            }
            assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
            return dest;
        }
    

    这里分开两条路,我们先看第一个

    autoreleaseNewPage

    先说一下大概流程

    1.获取一个活跃的页
    2.判断这页是不是满了,满的就新建一个page
    3.如果没获取到page,也是新建一个页
    4.把要push的obj,push到对应的页里

    1、
       id *autoreleaseNewPage(id obj)
        {
            //获取一个活跃的页
            AutoreleasePoolPage *page = hotPage();
            //如果有就返回,并且判断是否是满的,满的就会新建一个page
            if (page) return autoreleaseFullPage(obj, page);
            //如果没获取到page
            else return autoreleaseNoPage(obj);
        }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    2、
        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);
    
            // 如果满了就循环,这一页有child的话 就指向child,再继续判断是否满
            // 直到没有child 就创建一个新的页
            do {
                if (page->child) page = page->child;
                else page = new AutoreleasePoolPage(page);
            } while (page->full());
            // 设置成活跃的页
            setHotPage(page);
            // 将Obj 添加到这页里
            return page->add(obj);
        }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    3、
        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());
    
            // 翻译:pushExtraBoundary 推入一个额外边界
            bool pushExtraBoundary = false;
            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.
                //当push一个新页或者第一页的时候,在push之前需要先push一个poolboundary(边界)
                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.
                _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                             "autoreleased with no pool in place - "
                             "just leaking - break on "
                             "objc_autoreleaseNoPool() to debug", 
                             pthread_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.
                return setEmptyPoolPlaceholder();
            }
    
            // We are pushing an object or a non-placeholder'd pool.
    
            // Install the first page.
            AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
            setHotPage(page);
            
            // Push a boundary on behalf of the previously-placeholder'd pool.
            if (pushExtraBoundary) {
                page->add(POOL_BOUNDARY);
            }
            
            // Push the requested object or pool.
            return page->add(obj);
        }
    

    到此,第一条路就走完了。

    autoreleaseFast
    1、
        static inline id *autoreleaseFast(id obj)
        {
            //同样获取一个活页
            AutoreleasePoolPage *page = hotPage();
            if (page && !page->full()) {//如果page没满的话
                return page->add(obj);
            } else if (page) {//如果page满了
                return autoreleaseFullPage(obj, page);
            } else {//没有获取到活页
                return autoreleaseNoPage(obj);
            }
        }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    2、
        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);
    
            // 如果满了就循环,这一页有child的话 就指向child,再继续判断是否满
            // 直到没有child 就创建一个新的页
            do {
                if (page->child) page = page->child;
                else page = new AutoreleasePoolPage(page);
            } while (page->full());
            // 设置成活跃的页
            setHotPage(page);
            // 将Obj 添加到这页里
            return page->add(obj);
        }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    3、 
        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());
    
            // 翻译:pushExtraBoundary 推入一个额外边界
            bool pushExtraBoundary = false;
            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.
                //当push一个新页或者第一页的时候,在push之前需要先push一个poolboundary(边界)
                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.
                _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                             "autoreleased with no pool in place - "
                             "just leaking - break on "
                             "objc_autoreleaseNoPool() to debug", 
                             pthread_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.
                return setEmptyPoolPlaceholder();
            }
    
            // We are pushing an object or a non-placeholder'd pool.
    
            // Install the first page.
            AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
            setHotPage(page);
            
            // Push a boundary on behalf of the previously-placeholder'd pool.
            if (pushExtraBoundary) {
                page->add(POOL_BOUNDARY);
            }
            
            // Push the requested object or pool.
            return page->add(obj);
        }
    

    其实这两条路是差不多的,在底层中都走了这两个的方法
    autoreleaseFullPage
    autoreleaseNoPage
    感觉完全可以合起来啊- -,不知道苹果这么处理是为了什么。

    page->add()

    push的最终操作也就是这个add,看看这里都干了什么

        id *add(id obj)
        {
            assert(!full());
            unprotect(); //取消保护
            id *ret = next;  // faster than `return next-1` because of aliasing
            *next++ = obj;
            protect(); // 保护
            return ret;
        }
    

    这里做了什么呢

    1. 这里面有unprotect() protect() 这两个可以理解为加锁类似NSLock
    2. 创建一个临时变量ret 来存储next
    3. next ++ 内存地址移动。类似 0x0001 -> 0x0002
    4. 将obj存储到新移动出来的内存地址上

    那么push就先到这里,接下来说说autorelease

    autorelease

    在了解过push之后,我们可以很容易理解这个autorelease。看下面的代码跳转,
    autorelease最终走到了一个上文出现过的方法autoreleaseFast(),那么其实autorelease做的操作也跟push一样

    - (id)autorelease {
        return ((id)self)->rootAutorelease();
    }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    objc_object::rootAutorelease()
    {
        if (isTaggedPointer()) return (id)this;
        if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
    
        return rootAutorelease2();
    }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
    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;
    }
    

    AutoreleasepoolPage::Pop

    先简单说一下都做了什么

    1. 判断当前pop对象是否是空池占位符,是的话就推出去,按道理一般很少进去
    2. 通过栈顶的地址找到对应的page
    3. 记录最高水位标记
    4. 开始pop,objc_release(obj);
    5. kill空白页
        static inline void pop(void *token) 
        {
            AutoreleasePoolPage *page;
            id *stop;
            //只有在push的时候 push了一个边界符, 才有可能出现EMPTY_POOL_PLACEHOLDER
            if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
                // Popping the top-level placeholder pool.
                // 推出最顶上的占位池
                if (hotPage()) {
                    // Pool was used. Pop its contents normally.
                    // Pool pages remain allocated for re-use as usual.
                    // 递归 coldPage调用begin
                    pop(coldPage()->begin());
                } else {
                    // Pool was never used. Clear the placeholder.
                    setHotPage(nil);
                }
                return;
            }
    
            // 通过栈顶的地址找到对应的page
            page = pageForPointer(token);
            //token赋值给stop
            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 (PrintPoolHiwat) printHiwat();
            // 从栈顶开始操作出栈,并向栈中的对象发送release消息,直到遇到第一个边界符
            page->releaseUntil(stop);
    
            // memory: delete empty children
            // 删除空的child页
            if (DebugPoolAllocation  &&  page->empty()) {
                // special case: delete everything during page-per-pool debugging
                AutoreleasePoolPage *parent = page->parent;
                page->kill();
                setHotPage(parent);
            } else if (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();
                }
            }
        }
    
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
        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);
                }
    
                //类似push时候的操作
                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
        }
    ---------------------------------------- 华丽的分割线 ---------------------------------------------------------------------
        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;
            //如果page有child节点 就不断循环直到不再有child
            while (page->child) page = page->child;
    
            //创建一个deathptr临时变量来存储page
            AutoreleasePoolPage *deathptr;
            //如果page不等于this(可以理解成self)
            //就不断的循环把child节点一层一层的置为nil
            do {
                deathptr = page;
                page = page->parent;
                if (page) {
                    page->unprotect();
                    page->child = nil;
                    page->protect();
                }
                delete deathptr;
            } while (deathptr != this);
        }
    

    相关文章

      网友评论

          本文标题:iOS 自动释放池autoreleasepool(二)

          本文链接:https://www.haomeiwen.com/subject/jjxynqtx.html