美文网首页
探究Autorelease本质

探究Autorelease本质

作者: 我是C | 来源:发表于2018-09-26 12:02 被阅读50次

    一、先创建一个Dog类,重写dealloc方法,看看Dog类的实例什么时候释放。工程改为MRC 工程:

    image.png image.png

    发现实例并没有释放,在MRC 下需要添加 autorelease 或则 release

    image.png

    二、c++ 重写 main.m
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m -o main-arm64.cpp

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            Dog *g = ((Dog *(*)(id, SEL))(void *)objc_msgSend)((id)((Dog *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Dog"), sel_registerName("alloc")), sel_registerName("init"));
        }
        return 0;
    }
    
    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {//构造函数,在创建结构体时调用
          atautoreleasepoolobj = objc_autoreleasePoolPush();
      }
      ~__AtAutoreleasePool() {//析构函数,在销毁结构体时调用
          objc_autoreleasePoolPop(atautoreleasepoolobj);
      }
      void * atautoreleasepoolobj;
    };
    

    1.__AtAutoreleasePool() { atautoreleasepoolobj = objc_autoreleasePoolPush(); }:构造函数,在创建结构体时调用
    2.~__AtAutoreleasePool() { objc_autoreleasePoolPop(atautoreleasepoolobj); }:析构函数,在销毁结构体时调用

    3.所以上面代码可以改为:
    伪代码

    {
        __AtAutoreleasePool __autoreleasepool;
        atautoreleasepoolobj = objc_autoreleasePoolPush();
        Dog *g = [[[Dog alloc] init] autorelease];
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    

    __autoreleasepool 是一个局部变量,大括号结束就会调用objc_autoreleasePoolPop,将autorelease 对象释放。

    三、看源码实现

    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }
    
    void
    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
    
    class AutoreleasePoolPage 
    {
        PAGE_MAX_SIZE;
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    }
    

    以上源码只保留重要部分。


    704C76E6-6322-4DD3-9751-FA296AB96C79.png

    1.AutoreleasePoolPage 本质是这么一个结构,大小是4096字节。(PAGE_MAX_SIZE:4096,源码得到)。
    2.前7个变量都是8字节,剩下的4040字节存储着autorelease对象地址
    3.AutoreleasePoolPage * const parent; AutoreleasePoolPage *child;:由此可知当4040字节无法放下autorelease对象地址时,会扩展一个AutoreleasePoolPage,且该数据结构是,双向链表

    54B8E5AE-2A59-4604-8CC1-20FE67033480.png
    4.next 指针指向当前autorelease对象地址位置,每push 一个 对象,next 就会++。
    id *add(id obj)
        {
            assert(!full());
            unprotect();
            id *ret = next;  // faster than `return next-1` because of aliasing
            *next++ = obj;
            protect();
            return ret;
        }
    

    5.POOL_BOUNDARY 边界:# define POOL_BOUNDARY nil

        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;
        }
    

    每次push 对象的时候会先pushPOOL_BOUNDARY 边界。

        id * begin() {
            return (id *) ((uint8_t *)this+sizeof(*this));
        }
    
        id * end() {
            return (id *) ((uint8_t *)this+SIZE);
        }
    

    begin:7个成员变量之后的开始位置
    end:结束位置

    7.releaseUntil(begin()); :release 直到 begin

    hotPage():当前AutoreleasePoolPage
    coldPage:非当前AutoreleasePoolPage

    四、runloop 、 autorelease、子线程

    1.主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理。

    2.子线程默认不开启runloop,当产生autorelease对象时候,会将对象添加到AutoreleasePoolPage中,也就是不手动创建Autoreleasepool也能正确释放对象,线程销毁时release对象

        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);
            }
        }
    
        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;
            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.
                _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);
        }
    

    Autoreleasepool 销毁时机也就是autorelease 对象销毁时机

    相关文章

      网友评论

          本文标题:探究Autorelease本质

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