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.
//入栈 static inline void *push() { id *dest; //判断是否有pool if (slowpath(DebugPoolAllocation)) { // Each autorelease pool starts on a new pool page // 自动释放池从新池页面开始 //如果没有,则创建 dest = autoreleaseNewPage(POOL_BOUNDARY); } else { //压栈一个POOL_BOUNDARY,即压栈哨兵 dest = autoreleaseFast(POOL_BOUNDARY); } ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY); return dest; }
//******** autoreleaseNoPage方法 ******** 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; //判断是否是空占位符,如果是,则压栈哨兵标识符置为YES 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; } //如果对象不是哨兵对象,且没有Pool,则报错 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", objc_thread_self(), (void*)obj, object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } //如果对象是哨兵对象,且没有申请自动释放池内存,则设置一个空占位符存储在tls中,其目的是为了节省内存 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 = new AutoreleasePoolPage(nil); //设置page为当前聚焦页 setHotPage(page); // Push a boundary on behalf of the previously-placeholder'd pool. //压栈哨兵的标识符为YES,则压栈哨兵对象 if (pushExtraBoundary) { //压栈哨兵 page->add(POOL_BOUNDARY); } // Push the requested object or pool. //压栈对象 return page->add(obj); }
//添加自动释放对象,当页满的时候调用这个方法 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-while遍历循环查找界面是否满了 do { //如果子页面存在,则将页面替换为子页面 if (page->child) page = page->child; //如果子页面不存在,则新建页面 else page = new AutoreleasePoolPage(page); } while (page->full());
//出栈 static inline void pop(void *token) { AutoreleasePoolPage *page; id *stop; //判断对象是否是空占位符 if (token == (void*)EMPTY_POOL_PLACEHOLDER) { //如果当是空占位符 // Popping the top-level placeholder pool. //获取当前页 page = hotPage(); if (!page) { // Pool was never used. Clear the placeholder. //如果当前页不存在,则清除空占位符 return setHotPage(nil); } // Pool was used. Pop its contents normally. // Pool pages remain allocated for re-use as usual. //如果当前页存在,则将当前页设置为coldPage,token设置为coldPage的开始位置 page = coldPage(); token = page->begin(); } else { //获取token所在的页 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); } }
//释放到stop位置之前的所有对象 void releaseUntil(id *stop) { // Not recursive: we don't want to blow out the stack // 不是递归的:我们不想破坏堆栈 // if a thread accumulates a stupendous amount of garbage //判断下一个对象是否等于stop,如果不等于,则进入while循环 while (this->next != stop) { // Restart from hotPage() every time, in case -release // autoreleased more objects 每次从hotPage()重新启动,以防-release自动释放更多对象 //获取当前操作页面,即hot页面 AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it //如果当前页是空的 while (page->empty()) { //将page赋值为父节点页 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 }
//销毁 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;
inline id objc_object::rootAutorelease() { // 是否为小对象 if (isTaggedPointer()) return (id)this; if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2(); }
对象执行rootAutorelease2
1 2 3 4 5 6 7
__attribute__((noinline,used)) id objc_object::rootAutorelease2() { ASSERT(!isTaggedPointer()); return AutoreleasePoolPage::autorelease((id)this); }
最后执行的还是AutoreleasePoolPage这个对象,调用autorelease
1 2 3 4 5 6 7 8 9
public: static inline id autorelease(id obj) { ASSERT(obj); ASSERT(!obj->isTaggedPointer()); id *dest __unused = autoreleaseFast(obj); ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj); return obj; }
static void initializeTaggedPointerObfuscator(void) { if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) || // Set the obfuscator to zero for apps linked against older SDKs, // in case they're relying on the tagged pointer representation. DisableTaggedPointerObfuscation) { objc_debug_taggedpointer_obfuscator = 0; } else { // Pull random data into the variable, then shift away all non-payload bits. arc4random_buf(&objc_debug_taggedpointer_obfuscator, sizeof(objc_debug_taggedpointer_obfuscator)); // _OBJC_TAG_MASK 进行混淆 objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK; } }
LWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow) { if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false; bool transcribeToSideTable = false; //为什么有isa?因为需要对引用计数+1,即retain+1,而引用计数存储在isa的bits中,需要进行新旧isa的替换 isa_t oldisa; isa_t newisa; //重点 do { transcribeToSideTable = false; oldisa = LoadExclusive(&isa.bits); newisa = oldisa; //判断是否为nonpointer isa if (slowpath(!newisa.nonpointer)) { //如果不是 nonpointer isa,直接操作散列表sidetable ClearExclusive(&isa.bits); if (rawISA()->isMetaClass()) return (id)this; if (!tryRetain && sideTableLocked) sidetable_unlock(); if (tryRetain) return sidetable_tryRetain() ? (id)this : nil; else return sidetable_retain(); } // don't check newisa.fast_rr; we already called any RR overrides //dealloc源码 if (slowpath(tryRetain && newisa.deallocating)) { ClearExclusive(&isa.bits); if (!tryRetain && sideTableLocked) sidetable_unlock(); return nil; } uintptr_t carry; //执行引用计数+1操作,即对bits中的 1ULL<<45(arm64) 即extra_rc,用于该对象存储引用计数值 newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++ //判断extra_rc是否满了,carry是标识符 if (slowpath(carry)) { // newisa.extra_rc++ overflowed if (!handleOverflow) { ClearExclusive(&isa.bits); return rootRetain_overflow(tryRetain); } // Leave half of the retain counts inline and // prepare to copy the other half to the side table. if (!tryRetain && !sideTableLocked) sidetable_lock(); sideTableLocked = true; transcribeToSideTable = true; //如果extra_rc满了,则直接将满状态的一半拿出来存到extra_rc newisa.extra_rc = RC_HALF; //给一个标识符为YES,表示需要存储到散列表 newisa.has_sidetable_rc = true; } } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) { // Copy the other half of the retain counts to the side table. //将另一半存在散列表的rc_half中,即满状态下是8位,一半就是1左移7位,即除以2 //这么操作的目的在于提高性能,因为如果都存在散列表中,当需要release-1时,需要去访问散列表,每次都需要开解锁,比较消耗性能。extra_rc存储一半的话,可以直接操作extra_rc即可,不需要操作散列表。性能会提高很多 sidetable_addExtraRC_nolock(RC_HALF); }
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); return (id)this; }
retry: do { oldisa = LoadExclusive(&isa.bits); newisa = oldisa; //判断是否是Nonpointer isa if (slowpath(!newisa.nonpointer)) { //如果不是,则直接操作散列表-1 ClearExclusive(&isa.bits); if (rawISA()->isMetaClass()) return false; if (sideTableLocked) sidetable_unlock(); return sidetable_release(performDealloc); } // don't check newisa.fast_rr; we already called any RR overrides uintptr_t carry; //进行引用计数-1操作,即extra_rc-1 newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc-- //如果此时extra_rc的值为0了,则走到underflow if (slowpath(carry)) { // don't ClearExclusive() goto underflow; } } while (slowpath(!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock(); return false;
underflow: // newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement newisa = oldisa; //判断散列表中是否存储了一半的引用计数 if (slowpath(newisa.has_sidetable_rc)) { if (!handleUnderflow) { ClearExclusive(&isa.bits); return rootRelease_underflow(performDealloc); }
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) { ClearExclusive(&isa.bits); sidetable_lock(); sideTableLocked = true; // Need to start over to avoid a race against // the nonpointer -> raw pointer transition. goto retry; }
// Try to remove some retain counts from the side table. //从散列表中取出存储的一半引用计数 size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set // even if the side table count is now zero.
if (borrowed > 0) { // Side table retain count decreased. // Try to add them to the inline count. //进行-1操作,然后存储到extra_rc中 newisa.extra_rc = borrowed - 1; // redo the original decrement too bool stored = StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits); if (!stored) { // Inline update failed. // Try it again right now. This prevents livelock on LL/SC // architectures where the side table access itself may have // dropped the reservation. isa_t oldisa2 = LoadExclusive(&isa.bits); isa_t newisa2 = oldisa2; if (newisa2.nonpointer) { uintptr_t overflow; newisa2.bits = addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow); if (!overflow) { stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, newisa2.bits); } } }
if (!stored) { // Inline update failed. // Put the retains back in the side table. sidetable_addExtraRC_nolock(borrowed); goto retry; }
// Decrement successful after borrowing from side table. // This decrement cannot be the deallocating decrement - the side // table lock and has_sidetable_rc bit ensure that if everyone // else tried to -release while we worked, the last one would block. sidetable_unlock(); return false; } else { // Side table is empty after all. Fall-through to the dealloc path. } } //此时extra_rc中值为0,散列表中也是空的,则直接进行析构,即自动触发dealloc流程 // Really deallocate. //触发dealloc的时机 if (slowpath(newisa.deallocating)) { ClearExclusive(&isa.bits); if (sideTableLocked) sidetable_unlock(); return overrelease_error(); // does not actually return } newisa.deallocating = true; if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
id object_dispose(id obj) { if (!obj) return nil; // 销毁实例而不会释放内存 objc_destructInstance(obj); //释放内存 free(obj);
return nil; }
objc_destructInstance为了消耗实例对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
void *objc_destructInstance(id obj) { if (obj) { // Read all of the flags at once for performance. bool cxx = obj->hasCxxDtor(); bool assoc = obj->hasAssociatedObjects();
// This order is important. //调用C ++析构函数 if (cxx) object_cxxDestruct(obj); //删除关联引用 if (assoc) _object_remove_assocations(obj); //释放 obj->clearDeallocating(); } return obj; }
在内部判断是否有析构函数,如果有则调用。
是否有关联对象,有的花移除关联对象。
执行clearDeallocating
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
inline void objc_object::clearDeallocating() { //判断是否为nonpointer isa if (slowpath(!isa.nonpointer)) { // Slow path for raw pointer isa. //如果不是,则直接释放散列表 sidetable_clearDeallocating(); } //如果是,清空弱引用表 + 散列表 else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) { // Slow path for non-pointer isa with weak refs and/or side table data. clearDeallocating_slow(); }