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