AutoreleasePool 学习
clang -rewrite-objc main.c
请到苹果官网下载 objc4-706 版本,里面有 AutoreleasePool 源码。
__AtAutoreleasePool __autoreleasepool
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} // 构造函数
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} // 析构函数
void * atautoreleasepoolobj; // 上下文
};
=>
/* @autoreleasepool */ {
void *autoreleasepoolobj = objc_autoreleasePoolPush();
// do other thing
objc_autoreleasePoolPop(autoreleasepoolobj);
}
objc_autoreleasePoolPush(), objc_autoreleasePoolPop()
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
struct __AtAutoreleasePool 是一个中间对象,调用的是 AutoreleasePoolPage 的内容。
AutoreleasePoolPage
class AutoreleasePoolPage
{
// AutoreleasePoolPage 大小为一个页大小 4Gb,4096 kb,0x1000
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
magic_t const magic; // 校验 AutoreleasePoolPage 完整性
id *next; // 指向 AutoreleasePoolPage 栈结构下一个为空的地址
pthread_t const thread; // AutoreleasePoolPage 页所在的线程
AutoreleasePoolPage * const parent; // AutoreleasePoolPage 是一个双向链表,指向前置结点
AutoreleasePoolPage *child; // 指向后继结点
uint32_t const depth;
uint32_t hiwat;
}
// 返回哨兵对象的位置(当前 page 第一个 autorelease 对象的前一个位置)
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
// 返回当前 page 的最大内存地址
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
// 判断当前 page 是否没有 autorelease 对象
bool empty() {
return next == begin();
}
// 判断当前 page 是否已满
bool full() {
return next == end();
}
POOL_BOUNDARY or POOL_SENTINEL(哨兵对象, objc4_706 改为POOL_BOUNDARY)
POOL_SENTINEL 是 nil 的别名,指向 AutoreleasePoolPage 存放第一个 autorelease 对象的位置的前面位置。
在每个自动释放池初始化调用 objc_autoreleasePoolPush 的时候,都会把一个 POOL_SENTINEL push 到自动释放池的栈顶,并且返回这个 POOL_SENTINEL 哨兵对象。(push 到双向链表的最后一个结点 page 的栈顶)
而当方法 objc_autoreleasePoolPop 调用时,就会向自动释放池中的对象发送 release 消息,直到第一个 POOL_SENTINEL(从双向链表的最后一个结点 page 的栈顶释放直到遇到第一个哨兵对象。因为 AutoreleasePool 可以嵌套,通常用哨兵对象来实现优先释放内层 pool)
objc_autoreleasePoolPush()
void *objc_autoreleasePoolPush()
{
return AutoreleasePoolPage::push();
}
# define POOL_BOUNDARY nil
static inline void *push()
{
id *dest;
dest = autoreleaseFast(POOL_BOUNDARY);
return dest;
}
static inline id *autoreleaseFast(id obj)
{
// 获取双向链表最后一个 page 结点
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
// 若 page 不为空,且没有满,则插入哨兵对象或 autorelease 对象
return page->add(obj);
} else if (page) {
// 若 page 满了,则调用 autoreleaseFullPage,应该是 new 一个后继结点 page
return autoreleaseFullPage(obj, page);
} else {
// 若 page 不存在,则 new 一个 page,作为第一个结点
return autoreleaseNoPage(obj);
}
}
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
查找双向链表中第一个非满的 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);
}
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());
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// 不清楚为什么当 NO page 时,要添加两次 POOL_BOUNDARY
if (obj != POOL_BOUNDARY) {
page->add(POOL_BOUNDARY);
}
return page->add(obj);
}
AutoreleasePoolPage(AutoreleasePoolPage *newParent)
: magic(), next(begin()), thread(pthread_self()),
parent(newParent), child(nil),
depth(parent ? 1+parent->depth : 0),
hiwat(parent ? parent->hiwat : 0)
{
if (parent) {
parent->check();
assert(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
objc_autoreleasePoolPop(void *ctxt)
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
// 查找哨兵对象所在 page;依次释放 hotPage 到当前 page 哨兵对象前的 autorelease 对象
// 释放后继结点 page 内存空间。若当前 page 不满一半,则删除 page->child。若当前 page 满一半,则保留 page->child,删除 page->child-child 。
static inline void pop(void *token) {
AutoreleasePoolPage *page = pageForPointer(token);
id *stop = (id *)token;
page->releaseUntil(stop);
if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill();
} else if (page->child->child) {
page->child->child->kill();
}
}
}
// stop, hotPage, empty()
// 从 hotPage 开始依次取 next 的值与 stop 比较,若 next 遍历到 begin() 位置时,page 指向 parent,hotPage设为 parent,继续比较 next 的值与 stop 的值。这里只是切换 page,不会销毁 page 内存空间。
// 当找到 stop 后,会重置 hotPage 为 this。
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);
}
id obj = *--page->next;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
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
}
// 删除双向链表某个结点以及后继结点的算法
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; // 没有设置 null 是为了和 this 比较
} while (deathptr != this);
}
其它涉及的小知识点
*p++
-> . () 优先级属于一级
- 后置++ 前置++ 后置-- 前置-- 优先级属于二级(从右向左)