Autorelease详解

2020-11-21  本文已影响0人  lsj980Ya

一、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 了。

上一篇下一篇

猜你喜欢

热点阅读