iOS 底層解析weak的實現原理
參考地址---------:http://www.cocoachina.com/ios/20170328/18962.html
weak 實現原理的概括
Runtime維護了一個weak表,用於儲存指向某個物件的所有weak指標。weak表其實是一個hash(雜湊)表,Key是所指物件的地址,Value是weak指標的地址陣列,為什麼是指標的地址而不是直接是指標的陣列?我認為只得到指標沒法做後續更改指標的指向,比如置nil,或是內部演算法,整改指向都受限。
weak 的實現原理可以概括一下三步:
1、初始化時:runtime會呼叫objc_initWeak函式,初始化一個新的weak指標指向物件的地址。
2、新增引用時:objc_initWeak函式會呼叫 objc_storeWeak() 函式, objc_storeWeak() 的作用是更新指標指向,建立對應的弱引用表。
3、釋放時,呼叫clearDeallocating函式。clearDeallocating函式首先根據物件地址獲取所有weak指標地址的陣列,然後遍歷這個陣列把其中的資料設為nil,最後把這個entry從weak表中刪除,最後清理物件的記錄。
下面將開始詳細介紹每一步:
1、初始化時:runtime會呼叫objc_initWeak函式,objc_initWeak函式會初始化一個新的weak指標指向物件的地址。
示例程式碼:
1 2 3 4 |
|
當我們初始化一個weak變數時,runtime會呼叫 NSObject.mm 中的objc_initWeak函式。這個函式在Clang中的宣告如下:
1 |
|
而對於 objc_initWeak() 方法的實現
1 2 3 4 5 6 7 8 9 10 11 12 |
|
可以看出,這個函式僅僅是一個深層函式的呼叫入口,而一般的入口函式中,都會做一些簡單的判斷(例如 objc_msgSend 中的快取判斷),這裡判斷了其指標指向的類物件是否有效,無效直接釋放,不再往深層呼叫函式。否則,object將被註冊為一個指向value的__weak物件。而這事應該是objc_storeWeak函式乾的。
objc_initWeak、
objc_storeWeak 大致做了如下圖相關操作,更新維護兩張表,還需要加鎖保證多執行緒一致性,更新新舊雜湊表。
2、釋放時,呼叫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 2 3 4 5 6 7 |
|
也就是呼叫了clearDeallocating,繼續追蹤可以發現,它最終是使用了迭代器來取weak表的value,然後呼叫weak_clear_no_lock,然後查詢對應的value,將該weak指標置空,weak_clear_no_lock函式的實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
objc_clear_deallocating該函式的動作如下:
1、從weak表中獲取廢棄物件的地址為鍵值的記錄
2、將包含在記錄中的所有附有 weak修飾符變數的地址,賦值為nil
3、將weak表中該記錄刪除
4、從引用計數表中刪除廢棄物件的地址為鍵值的記錄
看了objc-weak.mm的原始碼就明白了:其實Weak表是一個hash(雜湊)表,然後裡面的key是指向物件的地址,Value是Weak指標的地址的陣列。