自动释放池压栈方法:
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {//debug环境下走
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);//压入哨兵对象
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
正常情况下会走autoreleaseFast(POOL_BOUNDARY),压入哨兵对象
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();//获取当前页表
if (page && !page->full()) {//如果当前页表存在且不满
return page->add(obj);//直接加入
} else if (page) {//pageFull处理
return autoreleaseFullPage(obj, page);
} else {//无page处理
return autoreleaseNoPage(obj);
}
}
当前hotPage页满时调用FullPage方法:
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());//断言,判断当前page是否为焦点页(hot)
ASSERT(page->full() || DebugPoolAllocation);
do {//顺着子节点找到最新的页面,并在最新孩子下方创建新的页
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);//设置当前新页为hot
return page->add(obj);//将对象加入页栈中
}
添加对象obj到page中
id *add(id obj)
{
ASSERT(!full());
unprotect();//解锁
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;//next指针存放然后++ 指针平移
protect();//上锁
return ret;
}
自动释放池出栈方法:
static inline void
pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
//如果当前为占位符(哨兵),则直接清空页就行了
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
page = coldPage();
token = page->begin();
} else {
page = pageForPointer(token);
}
//stop就是栈顶指针,指向end(),就会依次释放直到遇见哨兵
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
// popPage才是真正的处理
return popPage<false>(token, page, stop);
}
template<bool allowDebug>
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
if (allowDebug && PrintPoolHiwat) printHiwat();
// 释放页内对象
page->releaseUntil(stop);
// memory: delete empty children
// 释放完毕后清理并回收当前页
if (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
// 处理回收子节点
} else if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
回收页内存放对象:
void releaseUntil(id *stop)
{
// Not recursive: we don‘t want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can‘t prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
//回收对象内存
page->unprotect();//解锁
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();//上锁
if (obj != POOL_BOUNDARY) {
objc_release(obj);//释放
}
}
setHotPage(this);
回收页结构
void kill()
{
// Not recursive: we don‘t want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent; //变为父节点,一层一层往上
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;
} while (deathptr != this);
}