autorelease
说到 Objective-C 内存管理,就不能不提到 autorelease
。顾名思义,autorelease
就是自动释放。自动释放池是OC中的一种内存自动回收机制,它可以控制 autoreleasePool
中的变量release
的时机,简单来说,就是当创建一个对象,在正常情况下,变量会在超出其作用域的时立即 release
。如果将对象加入到了自动释放池中,这个对象并不会立即释放,会等到runloop
休眠/超出 autoreleasepool
作用域 {}
之后才会被释放。
autorelease 的使用方法如下:
@autoreleasepool {
#code
}
__AtAutoreleasePool
autorelease
是怎样实现的呢? 通过命令 xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m
编译文件生成 main.cpp
文件。源码如下:
struct __AtAutoreleasePool {
//构造函数
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
//析构函数
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
int main(int argc, const char * argv[]) {
{
//是一个结构体
__AtAutoreleasePool __autoreleasepool;
}
return 0;
}
通过编译器模拟代码可以看到, autoreleasepool
其实就是 一个__AtAutoreleasePool
的结构体,有构造函数和析构函数,结构体定义的对象在作用域结束后,会自动调用析构函数。autoreleasepool
在加入要释放的对象时,底层调用的是objc_autoreleasePoolPush
方法,在调用析构函数释放时,内部的实现是调用 objc_autoreleasePoolPop
方法
AutoreleasePoolPage
在 objc 源码中,搜索 objc_autoreleasePoolPush
与 objc_autoreleasePoolPop
源码如下:
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
可以看到两个函数都是通过 AutoreleasePoolPage
调用 push
和 pop
函数,查看 AutoreleasePoolPage
源码如下:
//************宏定义************
#define PAGE_MIN_SIZE PAGE_SIZE
#define PAGE_SIZE I386_PGBYTES
#define I386_PGBYTES 4096 /* bytes per 80386 page */
//************类定义************
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
friend struct thread_data_t;
public:
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MIN_SIZE; // size and alignment, power of 2 4096
#endif
private:
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const COUNT = SIZE / sizeof(id);
// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
// pushed and it has never contained any objects. This saves memory
// when the top level (i.e. libdispatch) pushes and pops pools but
// never uses them.
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
// SIZE-sizeof(*this) bytes of contents follow
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
static void operator delete(void * p) {
return free(p);
}
inline void protect() {
#if PROTECT_AUTORELEASEPOOL
mprotect(this, SIZE, PROT_READ);
check();
#endif
}
inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
check();
mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
}
//构造函数
AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
AutoreleasePoolPageData(begin(),
objc_thread_self(),
newParent,
newParent ? 1+newParent->depth : 0,
newParent ? newParent->hiwat : 0)
{
if (parent) {
parent->check();
ASSERT(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
//析构函数
~AutoreleasePoolPage()
{
check();
unprotect();
ASSERT(empty());
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
ASSERT(!child);
}
template
void
busted(Fn log) const
{
magic_t right;
log("autorelease pool page %p corrupted\n"
" magic 0x%08x 0x%08x 0x%08x 0x%08x\n"
" should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
" pthread %p\n"
" should be %p\n",
this,
magic.m[0], magic.m[1], magic.m[2], magic.m[3],
right.m[0], right.m[1], right.m[2], right.m[3],
this->thread, objc_thread_self());
}
__attribute__((noinline, cold, noreturn))
void
busted_die() const
{
busted(_objc_fatal);
__builtin_unreachable();
}
inline void
check(bool die = true) const
{
if (!magic.check() || thread != objc_thread_self()) {
if (die) {
busted_die();
} else {
busted(_objc_inform);
}
}
}
inline void
fastcheck() const
{
#if CHECK_AUTORELEASEPOOL
check();
#else
if (! magic.fastcheck()) {
busted_die();
}
#endif
}
//开始存储的位置
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
//存储结束的位置
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
//是否空页
bool empty() {
return next == begin();
}
//是否满页
bool full() {
return next == end();
}
//页的存储是否少于一半
bool lessThanHalfFull() {
return (next - begin() < (end() - begin()) / 2);
}
// 添加对象
id *add(id obj)
{
ASSERT(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
//释放所有对象
void releaseAll()
{
releaseUntil(begin());
}
//释放到stop位置之前的所有对象
void releaseUntil(id *stop)
{
//...
}
//结束
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);
}
//释放本地线程存储空间
static void tls_dealloc(void *p)
{
if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
// No objects or pool pages to clean up here.
return;
}
// reinstate TLS value while we work
setHotPage((AutoreleasePoolPage *)p);
if (AutoreleasePoolPage *page = coldPage()) {
if (!page->empty()) objc_autoreleasePoolPop(page->begin()); // pop all of the pools
if (slowpath(DebugMissingPools || DebugPoolAllocation)) {
// pop() killed the pages already
} else {
page->kill(); // free all of the pages
}
}
// clear TLS value so TLS destruction doesn't loop
setHotPage(nil);
}
//获取AutoreleasePoolPage
static AutoreleasePoolPage *pageForPointer(const void *p)
{
return pageForPointer((uintptr_t)p);
}
static AutoreleasePoolPage *pageForPointer(uintptr_t p)
{
AutoreleasePoolPage *result;
uintptr_t offset = p % SIZE;
ASSERT(offset >= sizeof(AutoreleasePoolPage));
result = (AutoreleasePoolPage *)(p - offset);
result->fastcheck();
return result;
}
//是否有空池占位符
static inline bool haveEmptyPoolPlaceholder()
{
id *tls = (id *)tls_get_direct(key);
return (tls == EMPTY_POOL_PLACEHOLDER);
}
//设置空池占位符
static inline id* setEmptyPoolPlaceholder()
{
ASSERT(tls_get_direct(key) == nil);
tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
return EMPTY_POOL_PLACEHOLDER;
}
//获取当前操作页
static inline AutoreleasePoolPage *hotPage()
{
//获取当前页
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);
//如果是一个空池,则返回nil,否则,返回当前线程的自动释放池
if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
//设置当前操作页
static inline void setHotPage(AutoreleasePoolPage *page)
{
if (page) page->fastcheck();
tls_set_direct(key, (void *)page);
}
//获取coldPage
static inline AutoreleasePoolPage *coldPage()
{
AutoreleasePoolPage *result = hotPage();
if (result) {
while (result->parent) {
result = result->parent;
result->fastcheck();
}
}
return result;
}
//快速 autorelease
static inline id *autoreleaseFast(id obj)
{
//...
}
//添加自动释放对象,当页满的时候调用这个方法
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
//...
}
//添加自动释放对象,当没页的时候使用这个方法
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
//...
}
//创建新页
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
//...
}
public:
//添加需要自动释放的对象
static inline id autorelease(id obj)
{
//...
}
//入栈
static inline void *push()
{
//...
}
//异常
__attribute__((noinline, cold))
static void badPop(void *token)
{
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
// OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
_objc_fatal
("Invalid or prematurely-freed autorelease pool %p.", token);
}
// Old SDK. Bad pop is warned once.
static bool complained = false;
if (!complained) {
complained = true;
_objc_inform_now_and_on_crash
("Invalid or prematurely-freed autorelease pool %p. "
"Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
"Proceeding anyway because the app is old "
"(SDK version " SDK_FORMAT "). Memory errors are likely.",
token, FORMAT_SDK(sdkVersion()));
}
objc_autoreleasePoolInvalid(token);
}
//出栈 Page
template
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();
}
}
}
__attribute__((noinline, cold))
static void
popPageDebug(void *token, AutoreleasePoolPage *page, id *stop)
{
popPage(token, page, stop);
}
//出栈
static inline void
pop(void *token)
{
//...
}
static void init()
{
int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
AutoreleasePoolPage::tls_dealloc);
ASSERT(r == 0);
}
//打印
__attribute__((noinline, cold))
void print()
{
_objc_inform("[%p] ................ PAGE %s %s %s", this,
full() ? "(full)" : "",
this == hotPage() ? "(hot)" : "",
this == coldPage() ? "(cold)" : "");
check(false);
for (id *p = begin(); p < next; p++) {
if (*p == POOL_BOUNDARY) {
_objc_inform("[%p] ################ POOL %p", p, p);
} else {
_objc_inform("[%p] %#16lx %s",
p, (unsigned long)*p, object_getClassName(*p));
}
}
}
//打印
__attribute__((noinline, cold))
static void printAll()
{
_objc_inform("##############");
_objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());
AutoreleasePoolPage *page;
ptrdiff_t objects = 0;
for (page = coldPage(); page; page = page->child) {
objects += page->next - page->begin();
}
_objc_inform("%llu releases pending.", (unsigned long long)objects);
if (haveEmptyPoolPlaceholder()) {
_objc_inform("[%p] ................ PAGE (placeholder)",
EMPTY_POOL_PLACEHOLDER);
_objc_inform("[%p] ################ POOL (placeholder)",
EMPTY_POOL_PLACEHOLDER);
}
else {
for (page = coldPage(); page; page = page->child) {
page->print();
}
}
_objc_inform("##############");
}
__attribute__((noinline, cold))
static void printHiwat()
{
// Check and propagate high water mark
// Ignore high water marks under 256 to suppress noise.
AutoreleasePoolPage *p = hotPage();
uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
if (mark > p->hiwat && mark > 256) {
for( ; p; p = p->parent) {
p->unprotect();
p->hiwat = mark;
p->protect();
}
_objc_inform("POOL HIGHWATER: new high water mark of %u "
"pending releases for thread %p:",
mark, objc_thread_self());
void *stack[128];
int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
char **sym = backtrace_symbols(stack, count);
for (int i = 0; i < count; i++) {
_objc_inform("POOL HIGHWATER: %s", sym[i]);
}
free(sym);
}
}
#undef POOL_BOUNDARY
};
从上面代码可以得知,AutoreleasePoolPage
继承于 AutoreleasePoolPageData
, 是一个 page
,其容量大小为 4096 字节。AutoreleasePoolPageData
结构体的内存大小为 56
字节,并且通过 parent
和 child
可以查找父子结点。AutoreleasePoolPageData
源码如下:
struct AutoreleasePoolPageData
{
//用来校验AutoreleasePoolPage的结构是否完整
magic_t const magic;//16个字节
//指向最新添加的autoreleased对象的下一个位置,初始化时指向begin()
__unsafe_unretained id *next;//8字节
//指向当前线程
pthread_t const thread;//8字节
//指向父节点,第一个结点的parent值为nil
AutoreleasePoolPage * const parent;//8字节
//指向子节点,最后一个结点的child值为nil
AutoreleasePoolPage *child;//8字节
//表示深度,从0开始,往后递增1
uint32_t const depth;//4字节
//表示high water mark 最大入栈数量标记
uint32_t hiwat;//4字节
//初始化
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
因此,autoreleasepool
其本质是一个结构体对象,一个自动释放池对象就是页,页内是栈结构存储,页与页之间是双向链表,符合先进后出的原则。页的栈底是一个 56 字节大小的空占位符,一页总大小为 4096 字节。只有第一页有哨兵对象,最多存储504 个对象,从第二页开始最多存储 505 个对象。autoreleasepool
在加入要释放的对象时,底层调用的是 objc_autoreleasePoolPush
方法。autoreleasepool
在调用析构函数释放时,内部的实现是调用 objc_autoreleasePoolPop
方法。
autorelease 实现
push()
objc_autoreleasePoolPush
会调用 AutoreleasePoolPage
的 push
函数进入压栈流程:
- 当
DebugPoolAllocation
为true
时,每一个自动释放池都需要从新的Pool Page
开始,调用autoreleaseNewPage
函数; - 当
DebugPoolAllocation
为false
时,进行压栈哨兵对象POOL_BOUNDARY
,调用autoreleaseFast
;
//入栈
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
autoreleaseNewPage()
autoreleaseNewPage
函数其实就是一个创建页并压栈对象的函数,调用 hotPage()
函数获取当前页后,判断 page
是否存在:
- 如果
page
存在,调用autoreleaseFullPage
函数压栈对象 - 如果
page
不存在,调用autoreleaseNoPage
函数创建页
//创建新页
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
//获取当前页
AutoreleasePoolPage *page = hotPage();
//如果存在,则压栈对象
if (page) return autoreleaseFullPage(obj, page);
//如果不存在,则创建页
else return autoreleaseNoPage(obj);
}
autoreleaseFast()
autoreleaseFast
函数看起来更像一个入口函数,获取当前页 page
,通过下面流程可以发现 autoreleaseFast
和 autoreleaseNewPage 函数类似,都会调用 autoreleaseFullPage
与 autoreleaseNoPage
函数,差别仅在于调用 add
函数:
- 如果当前
page
存在,并且页未填充满(!full),直接调用add
函数添加当前对象 - 如果当前
page
存在, 并且页满(full),调用autoreleaseFullPage
函数 - 如果当前
page
不存在,调用autoreleaseNoPage
函数
//快速 autorelease
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
autoreleaseFullPage()
autoreleaseFullPage
函数用于处理页满的情况,会根据链表子结点 child
寻找到最后一个结点,调用 AutoreleasePoolPage
构造函数后,将对象添加到新的 page
中
//添加自动释放对象,当页满的时候调用这个方法
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 {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
autoreleaseNoPage()
autoreleaseNoPage
用于创建第一页,调用 page
构造函数,判断当前 obj
是否为哨兵对象 POOL_BOUNDARY
,并进行压栈对象
//添加自动释放对象,当没页的时候使用这个方法
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// "No page" could mean no pool has been pushed
// or an empty placeholder pool has been pushed and has no contents yet
ASSERT(!hotPage());
bool pushExtraBoundary = false;
//判断是否是空占位符,如果是,则压栈哨兵标识符置为YES
if (haveEmptyPoolPlaceholder()) {
// We are pushing a second pool over the empty placeholder pool
// or pushing the first object into the empty placeholder pool.
// Before doing that, push a pool boundary on behalf of the pool
// that is currently represented by the empty placeholder.
pushExtraBoundary = true;
}
else if (obj != POOL_BOUNDARY && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
//如果对象不是哨兵对象,且没有Pool,则报错
_objc_inform("MISSING POOLS: (%p) Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
objc_thread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
// We are pushing a pool with no pool in place,
// and alloc-per-pool debugging was not requested.
// Install and return the empty pool placeholder.
//如果对象是哨兵对象,且没有申请自动释放池内存,则设置一个空占位符存储在tls中,其目的是为了节省内存
return setEmptyPoolPlaceholder();
}
// We are pushing an object or a non-placeholder'd pool.
// Install the first page.
//初始化第一页
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
//设置page为当前聚焦页
setHotPage(page);
// Push a boundary on behalf of the previously-placeholder'd pool.
if (pushExtraBoundary) {
//压栈哨兵的标识符为YES,则压栈哨兵对象
page->add(POOL_BOUNDARY);
}
// Push the requested object or pool.
// Push the requested object or pool.
//压栈对象
return page->add(obj);
}
总结,push
流程就是初始化 page
,保证当前存在 page
,并且 page
中有哨兵对象 POOL_BOUNDARY
,防止之后的出栈越界,流程如下图:
autorelease()
当 objc_autoreleasePoolPush()
函数 也就是 AutoreleasePoolPage::push
流程执行完后, 就会进入到自动释放池的作用域中,对每个创建的变量执行 autorelease
方法,通过 objc 源码查看,可以发现 autorelease
方法,最终调用 AutoreleasePoolPage::autorelease
函数,从而执行 autoreleaseFast
函数进行压栈对象。源码如下:
- (id)autorelease {
return _objc_rootAutorelease(self);
}
_objc_rootAutorelease(id obj)
{
ASSERT(obj);
return obj->rootAutorelease();
}
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
//添加需要自动释放的对象
static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
总结,整个流程 autorelease
-> _objc_rootAutorelease
-> rootAutorelease
-> rootAutorelease2
-> AutoreleasePoolPage::autorelease
-> autoreleaseFast
。
pop()
当离开自动释放池作用域时,会调用 AutoreleasePoolPage::pop
函数,获取 page
、找到 stop
指针为 page->begin()
,调用 popPage
函数,源码如下:
//出栈
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.
//如果当前页存在,则将当前页设置为coldPage,token设置为coldPage的开始位置
page = coldPage();
token = page->begin();
} else {
//获取token所在的页
page = pageForPointer(token);
}
//判断最后一个位置,是否是哨兵
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);
}
//出栈页
return popPage(token, page, stop);
}
popPage()
执行 popPage
流程,处理 page
,流程如下:
调用
releaseUntil
方法释放对象调用
kill
方法 释放page
//出栈 Page
template
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();
}
}
}
releaseUntil()
releaseUntil
递归 next
指针出栈所有 obj
并调用 objc_release
对其引用计数进行 -1 操作,直到 stop
指针位置
//释放到stop位置之前的所有对象
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);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
总结,当离开自动释放池作用域时,遍历 AutoRelesaePoolPage
中的对象进行出栈并对其调用 objc_relesae
,清空 Page
后,对 Page
进行 kill
。至此,整个 autoreleasePool 流程完成。