id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
bool full() {
return next == end();
}
SIZE的定义如下
#define I386_PGBYTES 4096 /* bytes per 80386 page */#define PAGE_SIZE I386_PGBYTES#define PAGE_MAX_SIZE PAGE_SIZE
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
add
add 功能是把对象添加到AutoreleasePoolPage
id *add(id obj)
{
assert(!full()); // 确保当前状态不满
unprotect(); // 设置为读/写状态
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect(); // 设置为只读状态
return ret;
}
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; // 如果 page->child 存在,则给 page 赋值 page->child
else page = new AutoreleasePoolPage(page); // 如果不存在,则新建一个AutoreleasePoolPage
} while (page->full());
setHotPage(page); // 设置新建的 AutoreleasePoolPage 为 hotPage
return page->add(obj); // 把对象添加到新建的 AutoreleasePoolPage
}
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;
if (haveEmptyPoolPlaceholder()) { // 如果当前有 poolPlaceholder
// 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;
}
elseif (obj != POOL_BOUNDARY && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
_objc_inform("MISSING POOLS: (%p) Object %p of class %s ""autoreleased with no pool in place - ""just leaking - break on ""objc_autoreleaseNoPool() to debug",
pthread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
elseif (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.
returnsetEmptyPoolPlaceholder(); // 设置占位符
}
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY); // 添加边界符,是一个nil,用来区别每个AutoreleasePoolPage的边界
}
// Push the requested object or pool.
return page->add(obj);
}
haveEmptyPoolPlaceholder
static inline bool haveEmptyPoolPlaceholder()
{
id *tls = (id *)tls_get_direct(key);
return (tls == EMPTY_POOL_PLACEHOLDER);
}
static void badPop(void *token)
{
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
// 对于旧的 SDK 来说, 这个错误并不是致命的
if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
// OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
// 对于开启 pool 内存分配的 debug 模式, 以及最新 SDK 的情况, 调用到 badPop 是错误的
_objc_fatal
("Invalid or prematurely-freed autorelease pool %p.", token);
}
// Old SDK. Bad pop is warned once.
// 旧 SDK 下, Bad pop 会记录一次日志
static bool complained = false;
if (!complained) {
complained = true;
// 输出一系列信息到 crash log 里, 但不会触发 crash
_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); // 摧毁包含 token 的自动释放池
}
releaseUntil
自动释放池销毁对象中最重要的一环, 调用者是用 pageForPointer() 找到的, token 所在的 page 节点, 参数为 token. 这个函数主要操作流程就是, 从 hotPage 开始, 使用 next 指针遍历存储在节点里的 autorelease 对象列表, 对每个对象进行一次 release 操作, 并且把 next 指向的指针清空, 如果 hotPage 里面的对象全部清空, 则继续循环向前取 parent 并继续用 next 指针遍历 parent, 一直到 next 指针指向的地址为 token 为止. 因为 token 就在 this 里面, 所以这个时候的 hotPage 应该是 this.
void releaseUntil(id *stop)
{
// 这里没有使用递归, 防止发生栈溢出
while (this->next != stop) { // 一直循环到 next 指针指向 stop 为止
// Restart from hotPage() every time, incase -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage(); // 取出 hotPage
while (page->empty()) { // 从节点 page 开始, 向前找到第一个非空节点
page = page->parent; // page 非空的话, 就向 page 的 parent 节点查找
setHotPage(page); // 把新的 page 节点设置为 HotPage
}
page->unprotect(); // 如果需要的话, 解除 page 的内存锁定
id obj = *--page->next; // 先将 next 指针向前移位, 然后再取出移位后地址中的值
memset((void*)page->next, SCRIBBLE, sizeof(*page->next)); // 将 next 指向的内存清空为SCRIBBLE
page->protect(); // 如果需要的话, 设置内存锁定
if (obj != POOL_BOUNDARY) { // 如果取出的对象不是边界符
objc_release(obj); // 给取出来的对象进行一次 release 操作
}
}
setHotPage(this); // 将本节点设置为 hotPage
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}