讀 Runtime 原始碼:物件與引用計數

Martin_wjl發表於2016-08-03

852671-fa0c5255dc2f7fcc

以前只是看了很多部落格,這次打算看一下原始碼,並記錄下來。想到哪裡就讀到哪裡,寫到哪裡。讀的程式碼版本是:objc runtime 680,可以從這裡下載

物件與 isa 指標

開始閱讀原始碼,首先 開啟 objc-private.h檔案,檢視對於 Objectiv-C 的物件的定義

每個物件都包含一個 isa指標,指向 isa_t 結構體,對isa_t結構體的內部一探究竟

可以看到原始碼裡面有一個#if SUPPORT_NONPOINTER_ISA來判斷是否支援isa指標優化,那麼來看一下SUPPORT_NONPOINTER_ISA的具體實現,開啟objc-config.h的82行可以看到

我們的電腦是 x86_64的處理器,那麼TARGET_IPHONE_SIMULATOR也是x86_64的處理器,那麼可以得知,目前只有arm64裝置支援isa優化,我們所使用的手機正是支援此優化

目前我們使用的裝置都是 64位的,也就是說isa 指標是一個64 bit的指標,那麼如果全用來存放記憶體地址就顯得有些浪費,於是蘋果有引入一種技術叫 Tagged Pointer

64位超大地址的出現,如果僅用來存放記憶體地址比較浪費,我們可以在指標地址中儲存或附加更多的資訊,這就是Tagged Pointer

那麼tagged pointer在 isa中有什麼運用呢?可以看出來 isa_t結構體中 64位並不是全部用來存放記憶體地址,到底怎麼放,來看一下 isa指標的初始化過程。

會根據傳入的indexed來判斷進行那種初始化方式,如果是indexed為0,則仍然按照以前的方式進行初始化,也就是訪問isa指標的時候,直接返回指向class的指標。也不會利用到剛才所講到Tagged Pointer

當indexed為1的時候,就會啟動優化isa指標優化,也就是說isa不再單單是類的指標,還包含更多的資訊,比如:引用計數、是否被weak引用等情況。

既然已經揭開了 結構體的面紗,就接著分析下每個變數所對應的含義吧

  • has_assoc
    • 表示該物件是否包含 關聯物件
  • has_cxx_dtor
    • 表示 該物件是否有 C++ 或者 Objc 的析構器
  • shiftcls
    • 類的指標
  • magic
    • 判斷物件是否初始化完成
  • weakly_referenced
    • 物件是否被指向一個弱變數
  • deallocating
    • 物件正在釋放記憶體
  • has_sidetable_rc
    • 判斷該物件的引用計數是否過大,如果過大則需要其他雜湊表來進行儲存
  • extra_rc
    • 存放該物件的引用計數值減一後的結果

引用計數

剛才說到 isa裡面儲存引用計數的問題,如果不支援isa優化,或者說,isa裡面儲存不夠用,這個時候就需要把引用計數交給SideTable去管理

對於引用計數計數的雜湊表定義如下

DenseMap是用來儲存引用計數,Key可以理解為物件的記憶體地址,value對應的是引用計數的值減 1

weak 表示弱引用,這個引用不會增加物件的引用計數,並且在物件釋放之後,weak指標被置為nil,好吧!這個都知道,但是內部具體是怎麼實現的呢?

weak

開啟objc-weak.h檔案可以看到以下程式碼

weak_table_t結構體儲存了與物件弱引用相關的資訊,weak_entry_t是負責來儲存物件弱引用關係的雜湊表

其中referent是被引用物件,union儲存了指向該物件的weak指標。由註釋可以知道,如果out_of_line等於0的時候,hash表被一個陣列所代替。

然後看一下,weak變數到底是怎麼初始化的,這個hash表又是怎麼利用起來的。

當我們初始化一個weak變數的時候,runtime會呼叫objc_initWeak函式

location代表的是_weak修飾的指標,而newObj則是一個物件。會首先進行一個判斷,如果newObj是一個空指標或者所指向的物件已經釋放了,那麼就會直接返回nil,也就是_weak指標變為nil

如果newObj是一個有效的物件,就會呼叫storeWeak方法,對原始碼整理了之後,如下

有點長呀!首先判斷是否存在weak指標以前是否指向舊物件,如果存在舊物件,就根據weak指標找到舊物件,並獲取舊物件的sideTable物件

獲取新物件的sideTable物件

然後就是在老物件的weak表中移除此weak變數的資訊,在新物件的weak表中與當前weak變數建立關係

最後讓_weak指標指向新物件,並返回 新物件

Strong

談到weak總會帶上strong,那也說一點吧!可以從NSObject.mm中看到objc_storeStrong的程式碼,也就是對當前指標所指向新舊物件的計數表進行操作

也就是根據當前strong指標指向的位置找到舊物件,然後對舊物件執行release操作,對新物件執行retain操作,並把strong指標從新指向新物件。retainrelease背後其實就是對引用計數的操作,下次再深入分析。

如果有理解錯誤,望留言告知

相關文章