ObjC物件的銷燬時間表
銷燬的開端
呼叫-release,release會呼叫:
uintptr_t objc_object::sidetable_release(bool performDealloc)
sidetable_release():
以下都是邏輯程式碼,完整程式碼得實現可以檢視runtime原始碼
加鎖 獲取當前物件所在的sidetable(一個hash表),在sidetable.refcnts(RefcountMap,一個map)中查到當前物件的迭代器 建立變數bool do_dealloc = false;代表是否需要dealloc 接著判斷迭代器是否是指向了sidetable的end 如果是就代表找不到: sidetable.refcnts[this] = SIDE_TABLE_DEALLOCATING(即計數變為0) do_dealloc = true else if(迭代器->second(物件的計數器)是否小於 SIDE_TABLE_DEALLOCATING){ second |= SIDE_TABLE_DEALLOCATING;do_dealloc = true } else if(迭代器->second & SIDE_TABLE_RC_PINNED > 0){ 計數器-=SIDE_TABLE_RC_ONE } 結束,解開鎖 if(do_dealloc&&performDealloc){ 呼叫物件的dealloc方法: ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc) } 返回do_dealloc
如果呼叫了dealloc:
進行以下過程時,不能再建立有新的 __weak引用,否則會crash
- 遞迴呼叫父類的-dealloc
如果是 MRC 程式碼,則需要手動釋放例項變數
-
最後呼叫NSObject的dealloc
NSObject的dealloc會呼叫_objc_rootDealloc(self);
_objc_rootDealloc(id obj)
if(obj是否活著){ 呼叫obj->rootDealloc() }else{ crash }
objc_object::rootDealloc()
if(this是使用TaggedPointer優化){ 直接返回 } if(不需要處理object_dispose的所有內容){ free(this)然後返回 } 呼叫 object_dispose((id)this)
object_dispose(id obj)
if(!obj){ 直接返回 } 呼叫 objc_destructInstance()
objc_destructInstance()
objc_destructInstance做的事情比較多,先說objc_destructInstance的內容,內部呼叫的方法後面再細說:
if(如果需要為 C++ 的例項變數們(iVars)呼叫析構器){ 呼叫object_cxxDestruct(obj) } if(通過obj->hasAssociatedObjects()判斷是否需要解除所有使用runtime Associate關聯的物件){ //如果是TaggedPointer優化,那這個方法也會返回true 呼叫_object_remove_assocations(obj) } 呼叫objc_clear_deallocating() 呼叫 free() 返回nil
object_cxxDestruct(obj)
if(!obj){return;} if(!isTaggedPointer){return;} 呼叫object_cxxDestructFromClass(obj, obj->ISA()); 內部會遞迴判斷自己和父類是否有.cxx_destruct方法,有的話則呼叫.cxx_destruct
.cxx_destruct
ARC下擁有例項變數才會有這個方法,通過Clang CodeGen生成,MRC都需要手動release所以不需要
ARC下會遍歷當前物件所有的例項變數通過objc_storeStrong() release掉
具體實現過程:ofollow,noindex">https://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/
_object_remove_assocations(obj)
關聯物件都存放在AssociationsHashMap中,以obj為key,以存放關聯物件的ObjectAssociationMap為value,具體操作就是把ObjectAssociationMap中的所有物件對應的Allocator拿出來,作為引數傳送給ReleaseValue(),然後呼叫objc_release()再對關聯物件傳送release
objc_clear_deallocating()
if(isa.nonpointer){ 呼叫sidetable_clearDeallocating()把物件的weak指標置nil,把物件的計數引用移除 } if (isa.weakly_referenced(是否有過弱引用)||isa.has_sidetable_rc(是否因為計數太大有多個sidetable)){ 呼叫clearDeallocating_slow();內部再分開判斷各自實現sidetable_clearDeallocating的內容 }