天天看點

iOS深入學習之Weak關鍵字介紹

從大二的開始接觸oc就用到了weak屬性修飾詞,但是當時隻是知道如何去用這個關鍵字:防止循環引用。根本沒有深入地去了解它。

在剛來北京的時候面試過程中也常常考到該知識點。大點的公司可能會問它如何使用?如何在對象銷毀後将對象置nil,小點的公司可能隻問一下它的使用。

now,如果你對它産生恐懼或者曾經對它産生過恐懼(+1),如果你被該關鍵字弄得整天吃不下飯,睡不着覺,那麼可以繼續往下閱讀,希望讀過該部落格之後能夠幫到你。

廢話不多說,開始介紹。

先來看看最簡單的一個例子:

我們可以看到此時輸出的結果為:

2017-02-07 13:20:41.119278 test[7341:2187477] result is :(null)

如果我們使用的strong來修飾weakpoint,此時輸出的結果為:

2017-02-07 13:23:13.211164 test[7344:2187993] result is :<uilabel: 0x100206070; frame = (0 0; 0 0); userinteractionenabled = no; layer = <_uilabellayer: 0x17009a590>>

如果我們使用assign來修飾weakpoint,此時運作程式可能會崩潰(因為如果引用操作發生時記憶體還沒有改變内容,依舊可以輸出正确結果,如果引用的時候記憶體内容發生改變了,就會crash),因為當assign指針所指向的記憶體被釋放之後,不會自動指派為nil,這樣再次引用該指針的時候就會導緻野指針操作。

對上述代碼運作結果進行分析:

當使用weak關鍵字的時候,不會增加對象的計數,而且當所指對象置nil的時候,使用weak修飾的指針将被指派為nil;

當使用strong關鍵字的時候,會增加對象的計數,也就是說會保持對象值的存在,是以當使用strong的時候weakpoint還會有值。

是以,我們從這裡可以得出一個結果:

strong是強引用,它會保持對象值的存在;

weak是弱引用,當weak指針指向的對象摧毀之後,屬性值也會清空(nil out)。

(注意:使用 _ _ weak修飾 和在@ property裡面設定weak是一樣的)

但是當我們執行如下代碼的時候:

你會發現隻有yourstring為空,其他兩個都不為空,這個是為什麼呢?原因如下

這裡是因為字元字面值永遠不會被釋放,是以你的weak指針還是指向它。

那麼請問weak指針指向對象被回收的時候該指針是如何被自動置為nil的呢??

首先,大家可以看一下部落格最後面的附錄,裡面有兩個文檔,嚴格來說是apple的opensouce。裡面有一個objc-weak的類。這裡是一個objc-weak.h類和一個objc-weak.mm類。

從.h中可以看到以下幾個關鍵的兩個結構體:weak_entry_t和weak_table_t,以及一些方法。接下來簡單介紹一下weak如何自動置為nil。

以下是從nsobject.mm裡面摘出的一些方法:

該function的作用是初始化一個新的weak指針指向對象的位址。其中的參數介紹如下:

location段:_ _ weak指針的位址

newobj:對象的指針位址

這裡調用的storeweak方法,storeweak方法裡面通過template模闆的參數進行更新weak操作,看源碼可以知道裡面會調用weak_register_no_lock/weak_unregister_no_lock等objc-weak.mm裡面的方法進行相應的操作。objc-weak.h裡面有句話:

對象被廢棄時最後調用objc_clear_deallocating。該函數實作如下:

也就是調用了cleardeallocating,繼續追蹤可以發現,它最終是使用了疊代器來取weak表的value,然後調用weak_clear_no_lock,然後查找對應的value,将該weak指針置空:

objc_clear_deallocating該函數的動作如下:

從weak表中擷取廢棄對象的位址為鍵值的記錄

将包含在記錄中的所有附有 _ _ weak修飾符變量的位址,指派為nil

将weak表中删除該記錄

從引用計數表中删除廢棄對象的位址為鍵值的記錄

看了objc-weak.mm的源碼大概了解了:其實weak表示一個hash表,然後裡面的key是指向對象的位址,value是weak指針的位址的數組。

以上便是我個人對weak的了解,檢視objc4的源碼,發現裡面更多的都是結構體、結構體套結構體等等。