美文网首页iOS 提升
autorelease底层实现

autorelease底层实现

作者: 昵称是乱起的 | 来源:发表于2019-01-27 18:17 被阅读10次
    先对@autoreleasepool { ... }进行clang -rewrite-objc main.m
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        ...
    }
     __AtAutoreleasePool的结构
    struct __AtAutoreleasePool {
        __AtAutoreleasePool() {//构造函数 在创建结构体的时候调用
            atautoreleasepoolobj = objc_autoreleasePoolPush();
        }
        ~__AtAutoreleasePool() {//析构函数,在结构体销毁的时候调用
            objc_autoreleasePoolPop(atautoreleasepoolobj);
        }
        void * atautoreleasepoolobj;
    };
    

    从上面可以得出autoreleasePool是这样工作的

    atautoreleasepoolobj = objc_autoreleasePoolPush();
    调用autorelease变量
    objc_autoreleasePoolPop(atautoreleasepoolobj);
    
    在 NSObject.mm 文件中可以找到这两个函数的实现
    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }
    void
    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
    
    push跟进
    static inline void *push() 
        {
            id *dest;
            if (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;
        }
    

    如果进来没有AutoreleasePoolPage对象,就创建一个AutoreleasePoolPage对象,并插入POOL_BOUNDARY,有的话就直接插入POOL_BOUNDARY

    先看下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:这个变量的类型是 magic_t,是用来检查 AutoreleasePoolPage 的内存没有被修改的,放在第一个也就是这个原因,防止前面地址有内容溢过来。
    next:类型是 id *,存放的是下一个被 autorelease 的对象指针存放的地址。
    thread:对应的线程,这说明了自动释放池是对应线程的。
    parent 和 child:用来保存前一个 AutoreleasePoolPage 和后一个 AutoreleasePoolPage,就是一个双向链表,毕竟一个 AutoreleasePoolPage 能存放的对象是有限的。
    depth:很明显是这个链表有多深。
    hiwat:一个在 DEBUG 时才有用的参数,表示最高有记录过多少对象(hi-water)。
    
    static size_t const SIZE = 
            PAGE_MAX_SIZE;  // size and alignment, power of 2  
    static void * operator new(size_t size) {
        return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
    }
    AutoreleasePoolPage 重载了 new 操作符,这样一个新的对象需要 SIZE 这么多的内存空间,SIZE 的大小 4096字节。AutoreleasePoolPage 的成员变量大小加在一起也只有 56 字节,但是 new 它一个居然要 4096 字节,这剩下的 4040 字节肯定就是存放被 autorelease 的对象的地方了。
    
    AutoreleasePoolPage创建的流程
    static __attribute__((noinline))
        id *autoreleaseNewPage(id obj)
        {
            AutoreleasePoolPage *page = hotPage();
            if (page) return autoreleaseFullPage(obj, page);
            else return autoreleaseNoPage(obj);
        }
    static inline AutoreleasePoolPage *hotPage() 
        {
            AutoreleasePoolPage *result = (AutoreleasePoolPage *)
                tls_get_direct(key);
            if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
            if (result) result->fastcheck();
            return result;
        }
    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);
        }
    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);
            }
        }
    

    autoreleaseFast跟上面操作一样,先获取一个AutoreleasePoolPage对象,然后插入POOL_BOUNDARY

    autorelease实现
    __attribute__((aligned(16)))
    id
    objc_autorelease(id obj)
    {
        if (!obj) return obj;
        if (obj->isTaggedPointer()) return obj;
        return obj->autorelease();
    }
    inline id 
    objc_object::autorelease()
    {
        if (isTaggedPointer()) return (id)this;
        if (fastpath(!ISA()->hasCustomRR())) return rootAutorelease();
        return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_autorelease);
    }
    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;
        }
    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);
            }
        }
    

    上面的代码做了一件事情,就是把autorelease的对象page->add(obj)

    再来看看pop的实现
    static inline void pop(void *token) 
        {
            AutoreleasePoolPage *page;
            id *stop;
    
            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.
                    pop(coldPage()->begin());
                } else {
                    // Pool was never used. Clear the placeholder.
                    setHotPage(nil);
                }
                return;
            }
    
            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 (PrintPoolHiwat) printHiwat();
    
            page->releaseUntil(stop);
    
            // memory: delete empty children
            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);
                }
    
                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
        }
    

    上面主要是把autorelease的对象一个一个清空,直到找到POOL_BOUNDARY分割,如果没有找到,就会根据parent找到上一个AutoreleasePoolPage对象,继续清空,知道找到POOL_BOUNDARY,最后把这个空的AutoreleasePoolPage对象也清空掉

    相关文章

      网友评论

        本文标题:autorelease底层实现

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