weak引用表原理探究
阿新 • • 發佈:2018-11-13
一、weak引用實現原理探究
首先對《Xcode 10 下如何除錯objc4-723》建立的objc原始碼除錯工程表示感謝!
地址:https://www.jianshu.com/p/9e0fc8295c4b
大多數文章闡述了基本過程:
1.初始化一個weak物件時,runtime會呼叫一個objc_initWeak函式,初始化一個新的weak指標指向該物件的地址 2.在objc_initWeak函式中會繼續呼叫objc_storeWeak函式,在這個過程是用來更新weak指標的指向,同時建立對應的弱引用表 3.在物件釋放時,會呼叫clearDeallocating函式,這個函式會根據物件地址獲取所有weak指標陣列,然後遍歷這個陣列置為nil。最後把該條物件的記錄從weak表中刪除。
id objc_initWeak(id *location, id newObj) { // 檢視物件例項是否有效 // 無效物件直接導致指標釋放 if (!newObj) { *location = nil; return nil; } // 這裡傳遞了三個 bool 數值 // 使用 template 進行常量引數傳遞是為了優化效能 return storeWeak<false/*old*/, true/*new*/, true/*crash*/> (location, (objc_object*)newObj); } template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating> static id storeWeak(id *location, objc_object *newObj) { assert(HaveOld || HaveNew); if (!HaveNew) assert(newObj == nil); Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; // Acquire locks for old and new values. // Order by lock address to prevent lock ordering problems. // Retry if the old value changes underneath us. retry: if (HaveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (HaveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable); if (HaveOld && *location != oldObj) { SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable); goto retry; } // Prevent a deadlock between the weak reference machinery // and the +initialize machinery by ensuring that no // weakly-referenced object has an un-+initialized isa. if (HaveNew && newObj) { Class cls = newObj->getIsa(); if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable); _class_initialize(_class_getNonMetaClass(cls, (id)newObj)); // If this class is finished with +initialize then we're good. // If this class is still running +initialize on this thread // (i.e. +initialize called storeWeak on an instance of itself) // then we may proceed but it will appear initializing and // not yet initialized to the check above. // Instead set previouslyInitializedClass to recognize it on retry. previouslyInitializedClass = cls; goto retry; } } // Clean up old value, if any. if (HaveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } // Assign new value, if any. if (HaveNew) { newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location, CrashIfDeallocating); // weak_register_no_lock returns nil if weak store should be rejected // Set is-weakly-referenced bit in refcount table. if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } // Do not set *location anywhere else. That would introduce a race. *location = (id)newObj; } else { // No new value. The storage is not changed. } SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable); return (id)newObj; }
其中涉及到一個數據結構
struct SideTable { spinlock_t slock; // 因為操作物件的引用計數頻率很快,因此係統在這裡設定了一把自旋鎖,保證是原子操作 RefcountMap refcnts; // 引用計數器雜湊表,根據物件地址查詢物件的引用計數 weak_table_t weak_table; // 維護weak指標的結構體 }
通過下面的程式碼取得
也就是全域性的sidetables本身是一個hash表,總共大小為64;每一個value對應的是 sidetable,sidetable中儲存引用計數表和weak引用表
找到一個sidetable表之後,要根據weak所指物件的地址hash值,找到對應儲存weak指標的value結構體
接下來的操作就是修改weak引用表了