Autorelease对象什么时候被释放?
在没有手动添加AutoreleasePool的情况下,Auturelease对象是在当前的runloop迭代结束的时候进行释放;而它能否释放的原因是系统在每个runloop的迭代中都加入了自动释放池的push和pop。
ARC下,使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其修改为下面的样子:
void *context = objc_autoreleasePoolPush();
//{}中的代码
objc_autoreleasePoolPop(context)
这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在与AutoreleasePoolPage这个类。

AutoreleasePool实际上是一个典型的双链表结构,每个page的大小为4096Byte;
自动释放池的工作过程:
void *objc_autoreleasePoolPush()内部实际调用的是AutoreleasePoolPage::push()函数,其实现如下:

hotPage()是找出当前正在使用的Page,第一次调用时hotPage为NULL,所以会新建一个parent=NULL的AutoreleasePoolPage对象作为自动释放池加入到栈中,并将其设置为hopPage,然后返回POOL_SENTINEL的地址赋值给main()函数里的变量__autoreleasepool;
然后将一个哨兵对象POOL_SENTINEL压入栈顶,即调用,即调用autoreleaseFast(POOL_SENTINEL)

添加对象进入到自动释放池
可以看出,当前有page并且没有满,则直接将对象入栈顶(page->add(obj))

将对象压入栈顶,然后将栈顶指针下移;
如果当前page已满,则调用autoreleaseShow

基本逻辑:如果page不存在则,创建新的自动释放池(PoolPage),并将对象加进池子;如果已经存在自动释放池在栈中,且hotPage满了,则遍历其子page,如果存在没满(page->full()==NO)的子page,则将该子page设置为hotPage,否则如果都满了,则以最后一个子page为父page,新建一个page,插入当前的page链表,同样设置该新建的page为hotPage,然后将自动释放对象加入page;
销毁自动释放池
void AutoreleasePoolPage::pop(void *token)
此处的token参数,即push()的返回值,实际上就是POOL_SENTINEL的地址;通过该地址即可找到所在Page的地址指针

该过程主要分为两步:
1,page->releaseUntil(stop); //对自动释放池中对象调用objc_release()进行释放,对栈顶到POOL_SENTINEL之间的所有对象调用objc_release(),进行引用计数减1;
2,page->kill(); //清空page对象
注意函数中的注释:
// memory: delete empty children
// hysteresis: keep one empty child if this page is more than half full
// special case: delete everything for pop(0)
保持一个空的子page当当前page大于一半时(可能是为了马上要新建page节省开销)
当调用pop(0)时,会清理掉所有的page;
if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill(); //如果当前存放的对象少于一半全部删除
}
else if (page->child->child) {
page->child->child->kill();//存放对象多余一半,则保留一个子page,节省开销
}
}
网友评论