一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
AutoreleasePoolPage的实现
AutoreleasePoolPage 是一个 C++ 中的类,它在 NSObject.mm 中的定义是这样的:
class AutoreleasePoolPage {
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
- magic 检查校验完整性的变量
- next 指向新加入的autorelease对象
- thread page当前所在的线程,AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)
- parent 父节点 指向前一个page
- child 子节点 指向下一个page
- depth 链表的深度,节点个数
- hiwat high water mark 数据容纳的一个上限
- EMPTY_POOL_PLACEHOLDER 空池占位
- POOL_BOUNDARY 是一个边界对象 nil,之前的源代码变量名是 POOL_SENTINEL哨兵对象,用来区别每个page即每个 AutoreleasePoolPage 边界* PAGE_MAX_SIZE = 4096, 为什么是4096呢?其实就是虚拟内存每个扇区4096个字节,4K对齐的说法。* COUNT 一个page里对象数
双向链表
AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成的栈结构(分别对应结构中的parent指针和child指针)
parent和child就是用来构造双向链表的指针。parent指向前一个page, child指向下一个page。
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入。
objc_autoreleasePoolPush
每当自动释放池调用objc_autoreleasePoolPush时都会把边界对象放进栈顶,然后返回边界对象,用于释放。
atautoreleasepoolobj = objc_autoreleasePoolPush();
atautoreleasepoolobj就是返回的边界对象(POOL_BOUNDARY)
push实现如下:
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
它调用AutoreleasePoolPage的类方法push:
static inline void *push() {
return autoreleaseFast(POOL_BOUNDARY);
}
在这里会进入一个比较关键的方法autoreleaseFast,并传入边界对象(POOL_BOUNDARY):
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);
}
}
上述方法分三种情况选择不同的代码执行:
- 有 hotPage 并且当前 page 不满,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
- 有 hotPage 并且当前 page 已满,调用 autoreleaseFullPage 初始化一个新的页,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
- 无 hotPage,调用 autoreleaseNoPage 创建一个 hotPage,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
最后的都会调用 page->add(obj) 将对象添加到自动释放池中。
hotPage 可以理解为当前正在使用的 AutoreleasePoolPage。