Runtime objc4-779.1 通過runtime源碼對OC對象銷燬過程解析
Step1 NSObject.mm line 2340
// Replaced by NSZombies
- (void)dealloc {
// Setp2
_objc_rootDealloc(self);
}
Step2 NSObject.mm line 1814
void
_objc_rootDealloc(id obj)
{
// 判斷對象是否真是存在,不存在則結束
ASSERT(obj);
// Step3
obj->rootDealloc();
}
Step3 objc-object.h line 433
inline void
objc_object::rootDealloc()
{
// 如果是tagged pointer 則結束, 因爲它不按照對象的內存管理來,僅僅是一個指針,指針中含有對象相關的信息,包含它的值, 如果不知道什麼個tagged pointer 可以看之前的博文 https://blog.csdn.net/wxs0124/article/details/82712478
if (isTaggedPointer()) return; // fixme necessary?
// 如果對象已成創建isa_t(初始化),沒有被weak修飾,沒有關聯其他對象,沒有實現C++析構方法,沒有因引用計數過多而存儲在sidetable中
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
//判定本對象不存在於sidetable中
assert(!sidetable_present());
//釋放
free(this);
}
else {
// 此步需要進一步解析
// Step4
object_dispose((id)this);
}
}
Step4 objc-runtime-new.mm line 7564
id
object_dispose(id obj)
{
if (!obj) return nil;
// 處理c++析構過程,移除對象關聯,clear
// Step5
objc_destructInstance(obj);
free(obj);
return nil;
}
Step5 objc-runtime-new.mm line 7542
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
// 是否有c++/OC析構函數
bool cxx = obj->hasCxxDtor();
// 是否有其他關聯
bool assoc = obj->hasAssociatedObjects();
// This order is important.
// 進行析構,詳見 Step6
if (cxx) object_cxxDestruct(obj);
// 移除所有相關的關聯對象,詳見 Step7
if (assoc) _object_remove_assocations(obj);
// 清理內存空間 詳見Step8
obj->clearDeallocating();
}
return obj;
}
Step6 objc-class.mm line 463
void object_cxxDestruct(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
// Step6.1
object_cxxDestructFromClass(obj, obj->ISA());
}
6.1 objc-class.mm line 437
static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);
// Call cls's dtor first, then superclasses's dtors.
// 從本類開始按照繼承鏈依次便利父類
for ( ; cls; cls = cls->superclass) {
// 如果沒有實現c++/OC析構方法,則結束
if (!cls->hasCxxDtor()) return;
// 在本類中找到析構方法並且記載到緩存中 , 方法實現在objc-class-old.mm line 578 此處不展開
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
// 因爲lookupMethodInClassAndLoadCache只會在本類中尋找方法(不會去父類),找不到則會返回objc_msgForward, _objc_msgForward_impcache是編譯器的消息轉發標記,代表此方法要走消息轉發,如果析構方法在本類中被找到了,則此一定爲true,進入代碼塊,執行代碼.
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);
}
}
}
Step7 objc-references.mm line 217
從關聯表中移除相關的關聯對象
void
_object_remove_assocations(id object)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
// 便利查找到和object相關的map並從其中移除記錄
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
refs.swap(i->second);
associations.erase(i);
}
}
// release everything (outside of the lock).
for (auto &i: refs) {
i.second.releaseHeldValue();
}
}
這裏的代碼實在associationHashMap中尋找當前對象相關關聯,並且擦出
association是一塊相對獨立且重要的知識點.牽扯關聯表的存儲格式,存取對象方式,流程,內存管理等,我們會在後邊的博客中詳細探討.此處不再展開
Step8 objc-object.h line 417
inline void
objc_object::clearDeallocating()
{
// 如果是隻有isa指針,沒有優化成isa_t
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// 如果沒有被弱引用,或者 在sidetable中查不到(意味着引用計數沒有太大,在isa_t中存儲)
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
Step8.1 NSObject.mm line 1552
清空在sidetable(用於存儲引用計數過多導致isa存不下的對象的應用計數信息)的數據
void
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
Step8.2 NSObject.mm line 1212
// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
總結一下:
- 釋放對象的核心方法爲
void *objc_destructInstance(id obj)
- 核心流程爲 1, 沿着繼承鏈調用c++/OC析構函數.2,移除與其相關聯的對象記錄.weak指針置爲nil.3,移除weak修飾的對象在weak_table的記錄並更新其引用計數數據.
- 萬變不離其宗,其中知識點依舊是老生常談的AssociationsHashMap,weak,引用計數.我們逐一擊破