AutoreleasePool 的数据结构是一个双向链表, 每个节点都是一个 AutoreleasePoolPage.
AutoreleasePoolPage 是一个栈结构的类, 保存的是添加进 AutoreleasePool 的对象内存地址.
AutoreleasePool 嵌套时, 并不是创建一个新的 pool, 而是当前 pool => hotPage 的栈顶插入一个边界对象, 代表内嵌 pool 的边界 (见下图3). pop 时会依次出栈, 直到(包括)内嵌 pool 的 边界对象(POOL_BBOUNDARY).
@autoreleasepool {
Person *p = [Person new];
}
等价于
void *atautoreleasepoolobj = objc_autoreleasePoolPush();
Person *p = [[Person new] autorelease];
objc_autoreleasePoolPop(atautoreleasepoolobj);
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
关键类: AutoreleasePoolPage 继承自 AutoreleasePoolPageData
next: 保存的是最后一个入栈对象的地址
thread: AutoreleasePool 是与线程绑定的, 代表当前线程
parent: 父节点
child: 子节点
depth: 链表深度, 节点个数
struct AutoreleasePoolPageData
{
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
struct AutoreleasePoolEntry {
uintptr_t ptr: 48;
uintptr_t count: 16;
static const uintptr_t maxCount = 65535; // 2^16 - 1
};
static_assert((AutoreleasePoolEntry){ .ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif
magic_t const magic;
// 保存的是最后一个入栈的对象地址
__unsafe_unretained id *next;
// 当前线程
pthread_t const thread;
// 双向链表
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
// 链表的深度,节点个数
uint32_t const depth;
uint32_t hiwat;
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)
{
}
};
push: AutoreleasePoolPage --> next 位置 插入边界对象: POOL_BOUNDARY
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;
}
autorelease: obj 入栈;
根据 hotPage (当前 page) 的状态有3种情况
1. hotPage 不为空 && 未满: 直接 add();
2. hotPage 已满: 创建新的 page 节点并关联 当前 page 与 新 page 的 child, parent
3. hotPage 为空: 创建一个 page 节点
然后移动 next;
static inline id autorelease(id obj)
{
ASSERT(!obj->isTaggedPointerOrNil());
id *dest __unused = autoreleaseFast(obj);
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || (id)((AutoreleasePoolEntry *)dest)->ptr == obj);
#else
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
#endif
return obj;
}
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);
}
}
最终都会调用 add 函数; next 指针移动到下一个位置, 该位置保存的是一个 id 类型的对象的地址
id *add(id obj) {
...
*(next++) = obj;
...
}