當一個 __weak 類型的指針指向的對象被釋放時,該指針會自動被置成nil,因此__weak關鍵字修飾的指針又被稱為智能指針。那么這個功能是如何實現的呢?
id __weak obj1 = obj;
會轉化為
id obj1;
objc_initWeak(&obj1, obj);
objc_destoryWeak(&obj1);
即編譯器會通過objc_initWeak函數初始化__weak修飾的變量,當變量的作用域結束后會通過objc_destoryWeak函數釋放該變量。objc_initWeak函數實際干的活是:
objc1 = 0;
objc_storeWeak(&obj1, obj);
這里是先將指針objc1置成0,再調用objc_storeWeak函數使得obj1指向obj對象。
接下來的objc_destoryWeak函數的實際操作如下:
objc_storeWeak(&obj1, 0);
也就是說,讓obj1指針指向的內容變成空。
__weak實現原理
實際上,objc_storeWeak函數會把第二個參數的對象的地址作為key,并將第一個參數(__weak關鍵字修飾的指針的地址)作為值,注冊到weak表中。如果第二個參數為0(說明對應的對象被釋放了),則將weak表中將整個key-value鍵值對刪除,這就是__weak關鍵字的核心思想!
weak表和引用計數表類似,都是通過hash表實現的。如果使用weak表,將被釋放的對象地址作為key去檢索,就能很高效的獲取對應的指向該對象的類型為__weak的指針變量的地址。同時很容易理解,一個對象可能有多個__weak指針指向,因此一個對象地址key可能對應多個值。
在調用對象的release方法時,會在其中一步調用objc_clear_deallocating函數,該函數會執行以下操作:以當前對象的地址作為key,從weak表中獲取對應的值----指向該對象的__weak類型的指針變量;將取到的所有指針變量的值賦值為nil;從weak表中刪除該key對應的整條記錄。
如果大量使用附有__weak修飾符的變量會消耗響應的CPU資源,因此,應該盡量少使用__weak修飾符。