美文网首页
Autorelease详解

Autorelease详解

作者: lsj980Ya | 来源:发表于2020-11-21 15:59 被阅读0次

    一、AutoreleasePool是什么

    AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机。在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release将延迟执行(我们后面解答具体什么时候执行)

    二、AutoreleasePool的实现

    @autoreleasepool {
         NSLog("Hello World");
    }
    

    上面这段代码对于iOSer来说已经是非常熟悉了,但是这段代码到底做了什么?我们知道OC底层是封装了C++,我们尝试用clang 命令去转换成C++代码得到如下(NSLog不是重点直接原样输出了):

    //__AtAutoreleasePool 结构体
    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    
    {
       //C++语法
       //这句话相当于通过构造函数构(__AtAutoreleasePool)创建一个栈变量__autoreleasepool
       //在大括号结束的时候通过析构函数(~__AtAutoreleasePool)销毁__autoreleasepool变量
       //既 NSLog 是放在构造函数和析构函数之间的
       __AtAutoreleasePool __autoreleasepool; 
       NSLog("Hello World");
    }
    

    把构造函数 __AtAutoreleasePool()和 析构函数~ __AtAutoreleasePool ()函数中的逻辑替换得到

    {
       atautoreleasepoolobj = objc_autoreleasePoolPush();
       NSLog("Hello World");
       [obj1 autorelease];
       [obj2 autorelease];
      //这一步骤是释放自动释放池中的对象obj1,obj2
       objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    

    至此,我们可以分析出,单个自动释放池的执行过程就是objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)

    三、AutoreleasePool源码解析

    首先我们看到入口函数是objc_autoreleasePoolPush,我们在runtime的开源项目中搜索一下该函数

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

    从上我们可以看到objc_autoreleasePoolPush调用了AutoreleasePoolPage::push()方法。在这里我们看到了一个新的结构体AutoreleasePoolPage

    struct AutoreleasePoolPage
    {
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    }
    

    这是因为AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成 AutoreleasePool是按线程一一对应的 。AutoreleasePoolPage每个对象会开辟4096字节内存 autorelease对象, *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入

    如下图所示:

    AutoreleasePoolPage

    AutoreleasePoolPage::push方法又分别调用了autoreleaseNewPage(POOL_BOUNDARY)autoreleaseFast(POOL_BOUNDARY)方法,并且传入了POOL_BOUNDARY作为哨兵对象,既临界值,在销毁对象的时候会用到

    static inline void *push() 
    {
            id *dest;
            if (slowpath(DebugPoolAllocation)) {
                //创建一个新的page,并存储一个哨兵对象
                dest = autoreleaseNewPage(POOL_BOUNDARY);
            } else {
                //存储一个哨兵对象
                dest = autoreleaseFast(POOL_BOUNDARY);
            }
            ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
            return dest;
    }
    
    static __attribute__((noinline))
    id *autoreleaseNewPage(id obj)
    {
            //获得当前的hotPage
            AutoreleasePoolPage *page = hotPage();
            //对于该函数,每次都会创建一个新的page,然后把新的page设置为hotPage
            if (page) return autoreleaseFullPage(obj, page);
            else return autoreleaseNoPage(obj);//如果page不存在,则创建一个新的page
    }
    
    static inline id *autoreleaseFast(id obj)
    {
            AutoreleasePoolPage *page = hotPage();
            if (page && !page->full()) {//当前hotPage存在且未满,直接添加到当前hotPage
                return page->add(obj);
            } else if (page) {//当前hotPage存在且满了,则创建一个新的page并设置为hotPage,然后添加对象到hotPage
                return autoreleaseFullPage(obj, page);
            } else {//hotPage不存在创建一个hotPage
                return autoreleaseNoPage(obj);
            }
    }
    
    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 {
                //如果page的child存在
                if (page->child) page = page->child;
                else page = new AutoreleasePoolPage(page);
            } while (page->full());
            //设置为hotPage
            setHotPage(page);
            return page->add(obj);
    }
    
    id *add(id obj)
    {
            ASSERT(!full());
            unprotect();
            id *ret = next;  // faster than `return next-1` because of aliasing
            //存储到hotPage,并且next++
            *next++ = obj;
            protect();
            return ret;
    }
    

    四、添加autorelease对象到AutoreleasePoolPage中

    MRC下,在我们调用autorelease的时候回自动把对象加入到AutoreleasePoolPage中,对应的函数调用堆栈是
    obj->autorelease->_objc_rootAutorelease->rootAutorelease->rootAutorelease2->AutoreleasePoolPage::autorelease()->autoreleaseFast()
    从函数堆栈可以看到,每当我们调用[obj autorelease]时最终都会走到autoreleaseFast,然后把点前对象添加到AutoreleasePoolPage中去

    五、Autoreleasepool和Runloop的关系

    App 启动后,苹果在主线程 RunLoop 里注册了两个 Observer
    其回调都是 _wrapRunLoopWithAutoreleasePoolHandler() 。
    第一个 Observer 监视的事件是 Entry (即将进入 Loop ),其回调内会调用 _objc_autoreleasePoolPush()创建自动释放池。其 order 是 -2147483647 ,优先
    级最高,保证创建释放池发生在其他所有回调之前。
    第二个 Observer 监视了两个事件: BeforeWaiting (准备进入休眠) 时调用 _objc_autoreleasePoolPop()_objc_autoreleasePoolPush()释放旧的池并创建 新池; Exit (即将退出 Loop ) 时调用_objc_autoreleasePoolPop()来释放自动释放
    池。这个 Observer 的 order 是 2147483647 ,优先级最低,保证其释放池子发生在 其他所有回调之后。
    在主线程执行的代码,通常是写在诸如事件回调、 Timer 回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不
    必显示创建 Pool 了。

    相关文章

      网友评论

          本文标题:Autorelease详解

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