/*********************************************************************** Autorelease pool implementation
A thread's autorelease pool is a stack of pointers. Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary. A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released. The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary. Thread-local storage points to the hot page, where newly autoreleased objects are stored. **********************************************************************/
1. 解释
线程的自动释放池是一个指针栈
每个指针代表了要释放的对象或者是POOL_BOUNDARY(自动释放池的边界对象)
自动释放池pop时,在POOL_BOUNDARY之后push进来的对象会被释放
这个栈由若干双向列表构成。每页会在在必要的情况下增、删
TLS(Thread-local storage)指向最近储存的page(hotpage)
TLS: Thread-local storage (TLS) is a computer programming method that uses static or global memory local to a thread.
class AutoreleasePoolPage { // 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 //分界线 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);
id *add(id obj) { assert(!full()); unprotect();// id *ret = next; // faster than `return next-1` because of aliasing *next++ = obj;//并没有判断obj==nil protect(); return ret; }
autoreleaseFullPage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
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. //该页已满,向链表的child方向遍历,直到找到不满的page为止,如果遍历完还是没有符合要求的page,则new一个,并标记为hotPage, //在hotpage中加入obj
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()) { // 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); returnnil; } 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. return setEmptyPoolPlaceholder(); }
// We are pushing an object or a non-placeholder'd pool.
// Install the first page. //new一个并标记为hotpage AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); setHotPage(page); // Push a boundary on behalf of the previously-placeholder'd pool. //当 pushExtraBoundary 加入一个nil对象 作为边界标记 if (pushExtraBoundary) { page->add(POOL_BOUNDARY); } // Push the requested object or pool. //obj加入 return page->add(obj); }
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); }
#if DEBUG // we expect any children to be completely empty for (AutoreleasePoolPage *page = child; page; page = page->child) { assert(page->empty()); } #endif }