iOS中runtime如何實現weak變數的自動置nil和SideTable是什麼?

語言: CN / TW / HK

這是我參與11月更文挑戰的第9天,活動詳情檢視:2021最後一次更文挑戰

runtime對註冊的類進行佈局,對於weak修飾的物件會放入一個hash表中。用weak指向的物件記憶體地址作為key,當此物件的引用計數為0的時候會dealloc,假如weak指向的物件記憶體地址是a,那麼就會以a為鍵,在這個weak表中搜索,找到所有以a為鍵的weak物件,從而設定為nil。

說的更詳細一點的話:\ 1.初始化時:runtime會呼叫objc_initWeak函式,初始化一個新的weak指標指向物件的地址。\ 2.新增引用時:objc_initWeak函式會呼叫objc_storeWeak()函式,objc_storeWeak()的作用是更新指標指向,建立對應的弱引用表。\ 3.釋放時,呼叫clearDeallocating函式。clearDeallocating函式首先根據物件地址獲取所有weak指標地址的陣列,然後遍歷這個陣列把其中的資料設為nil,最後把這個entry從weak表中刪除,最後清理物件的記錄。

SideTable

SideTable結構體是負責管理類的引用計數表和weak表。\ 詳解:參考自《Objective-C高階程式設計》一書。\ 1.初始化時:runtime會呼叫objc_initWeak函式,初始化一個新的weak指標指向物件的地址。

{ NSObject *obj = [[NSobject alloc] init]; id __weak obj1 = obj; } 當我們初始化一個weak變數時,runtime會呼叫NSObject.mm中的objc_initWeak函式。

// 編譯器的模擬程式碼 id obj1; objc_initWeak(&obj1, obj); /*obj引用計數為0,變數作用域結束*/ objc_destoryWeak(&obj1); 通過objc_initWeak函式初始化“附有weak修飾符的變數(obj1)”,在變數作用域結束時通過objc_destoryWeak函式釋放該變數(obj1)。

2.新增引用時:objc_initWeak函式會呼叫objc_storeWeak()函式,objc_storeWeak()的作用是更新指標指向,建立對應的弱引用表。

objc_initWeak函式將“附有weak修飾符的變數(obj1)”初始化為0(nil)後,會將“賦值物件”(obj)作為引數,呼叫objc_storeWeak函式。

obj1 = 0; obj_storeWeak(&obj1, obj); 也就是說:\ weak修飾的指標預設值是nil(在Objective-C中向nil傳送訊息是安全的)\ 然後obj_storeWeak函式將0(nil)作為引數,呼叫objc_storeWeak函式。

objc_storeWeak(&obj1, 0); 前面的原始碼與下列原始碼相同。

//編譯器的模擬程式碼 id obj1; obj1 = 0; objc_storeWeak(&obj1, obj); /*... obj的引用計數變為0,被置nil...*/ objc_storeWeak(&obj1, 0); objc_storeWeak函式把第二個引數的賦值物件(obj)的記憶體地址作為鍵值,將第一個引數__weak修飾的屬性變數(obj1)的記憶體地址註冊到weak表中。如果第二個引數(obj)為0(nil),那麼把變數(obj1)的地址從weak表中刪除。\ 由於一個物件可同時複製給多個附有__weak修飾符的變數中,所以對於一個鍵值,可註冊多個變數的地址。\ 可以把objc_storeWeak(&a, b)理解為:objc_storeWeak(value, key),並且當key變nil,將value置nil。在b非nil時,a和b指向同一個記憶體地址,在b變nil時,a變nil。此時向a傳送訊息不回崩潰:在Objective-C中向nil傳送訊息是安全的。

3.釋放時,呼叫clearDeallocating函式。clearDeallocating函式首先根據物件地址獲取所有weak指標地址的陣列,然後遍歷這個陣列把其中的資料設為nil,最後把這個entry從weak表中刪除,最後清理物件的記錄。

當weak引用指向的物件被釋放時,又是如何去處理weak指標的呢?當釋放物件時,其基本流程如下:\ 1.呼叫objc_release\ 2.因為物件的引用計數為0,所以執行dealloc\ 3.在dealloc中,呼叫了_objc-rootDealloc函式\ 4.在_objc_rootDealloc中,呼叫了object_dispose函式\ 5.呼叫objc_destructinstance\ 6.最後呼叫objc_clear_deallocating

物件被釋放時呼叫的objc_clear_deallocating函式:\ 1.從weak表中獲取廢棄物件的地址為鍵值的記錄\ 2.將包含在記錄中的所有附有weak修飾符變數的地址,賦值為nil\ 3.將weak表中該記錄刪除\ 4.從引用計數表中刪除廢棄物件的地址為鍵值的記錄

總結\ 其實Weak表是一個hash(雜湊)表,Key是weak所指物件的地址,Value是weak指標的地址(這個地址的值是所指物件指標的地址)陣列