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); }
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; } else if (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; } else if (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. AutoreleasePoolPage *page = newAutoreleasePoolPage(nil); setHotPage(page); // Push a boundary on behalf of thepreviously-placeholder'd pool. if (pushExtraBoundary) { page->add(POOL_BOUNDARY); } // Push the requested object or pool. return page->add(obj); }
static inline void pop(void *token) { AutoreleasePoolPage *page; id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) { if (hotPage()) { // Pool was used. Pop its contents normally. // Pool pages remain allocated for re-use as usual. pop(coldPage()->begin()); } else { // Pool was never used. Clear the placeholder. setHotPage(nil); } return; } // 拿到 token 边界对应的 page page = pageForPointer(token); stop = (id *)token; if (*stop != POOL_BOUNDARY) { if (stop == page->begin() && !page->parent) { // Start of coldest page may correctly not be POOL_BOUNDARY: // 1. top-level pool is popped, leaving the cold page in place // 2. an object is autoreleased with no pool } else { // Error. For bincompat purposes this is not // fatal in executables built with old SDKs. return badPop(token); } }
if (PrintPoolHiwat) printHiwat(); // pop 内部对象直到 stop 边界 page->releaseUntil(stop);
// memory: delete empty children if (DebugPoolAllocation && page->empty()) { // special case: delete everything during page-per-pool debugging AutoreleasePoolPage *parent = page->parent; page->kill(); setHotPage(parent); } else if (DebugMissingPools && page->empty() && !page->parent) { // special case: delete everything for pop(top) // when debugging missing autorelease pools page->kill(); setHotPage(nil); } // 删除 page 时的策略选择 else if (page->child) { // hysteresis: keep one empty child if page is more than half full if (page->lessThanHalfFull()) { page->child->kill(); } else if (page->child->child) { page->child->child->kill(); } } }
result = (AutoreleasePoolPage *)(p - offset); result->fastcheck();
return result; }
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); }
page->unprotect(); id obj = *--page->next; memset((void*)page->next, SCRIBBLE, sizeof(*page->next)); page->protect();
if (obj != POOL_BOUNDARY) { objc_release(obj); } } setHotPage(this); }