iOS探索:Runtime之基本資料結構

熊貓超人發表於2018-12-11

objc_object

國際慣例,流程圖

WX20181211-150059@2x.png

  • 首先平時我們所使用的物件都是id型別的,id對應到runtime中就是objc_object這樣的一個結構體,在這個結構體當中主要包含以下幾個部分:

    1、第一部分是isa_t,實際上呢isa_t是一個共用體

    2、在objc_object中還包含了關於isa操作相關的方法,比如說通過objc_object這個結構體來獲取isa所指向的類物件,通過類物件的isa指標獲取它的元類物件

    3、還包含了一些弱引用相關的一些方法,比如說標記一個物件它是否有過弱引用指標

    4、還有關於關聯物件的一些方法,比如說為一個物件我們設定一些關聯屬性,關於關聯屬性的一些方法也體現在objc_object這個結構體中

    5、最後還包括記憶體管理相關的一些方法,比如說我們再MRC下經常使用到的retain和relese方法,包括MRC和ARC下面都可以用到的autorelesepool(自動釋放池),這些關於記憶體管理相關的方法都是封裝在objc_object這個結構體中

objc_class

WX20181211-151829@2x.png

  • 我們在OC語言中所使用的Class,代表的是一個類,在runtime中對應的是objc_class這樣一個結構體,它繼承自objc_object,那麼問題來了:Class這樣一個類,它是否是一個物件呢?答案當然是肯定的,我們稱之為類物件,因為它繼承自objc_object

  • 在objc_class這個結構體中,包含superClass這樣一個指標,它指向的也是一個Class,例如:如果這個Class是一個類物件,那麼它的superClass指標指向的事它的父類物件,一個類物件與它的父類物件也正是通過這個superClass指標來建立起聯絡

  • 接下來它還包含一個cache_t這樣一個成員變數,cache_t它表達了方法快取的一個結構,我們在進行訊息傳遞的時候會使用到對於方法快取的這樣一個資料結構

  • 第三個是關於class_data_bits_t這樣的一個資料結構,實際上我們對於一個類所定義的變數,屬性,包括它的一些方法都在bits這樣一個資料結構當中

isa指標

共用體isa_t

WX20181211-153729@2x.png

  • isa的結構主要是C++中的共用體,我們在OC當中實際上是定義成了一個isa_t這樣一個名稱,對於一個共用體來說,不論它是在64位架構還是在32位架構上面,它實際上都是32個0或者1的數字(或者64個0或者1的數字)

  • isa指標主要分為兩種:一種是指標型isa,另一種是非指標型isa

  • 指標型isa:isa的值代表的是Class的地址,也就是64個(或者32,看架構)0或者1它的整體內容所代表的是所指向的Class的地址

  • 非指標型isa:isa的值的部分代表的是Class的地址,比如說針對64位架構來說,可能對應的是某一部分33位或者44位所代表的值,並不是整個64位都代表,這樣做的一個目的是我們在定址過程中只有三四十位的位數就可以保證我們找到所有Class的地址,多出來的這些位可以用來儲存其他的內容,達到節省記憶體的目的

isa的指向

  • 關於物件,isa指向的是其類物件

  • 關於類物件,isa指向的是其元類物件

cache_t

  • 用於快速查詢方法執行函式 例如我們在呼叫一個方法的時候,如果這個方法已經快取,我們就不用去其對應的方法列表中遍歷查詢,從而節省時間

  • 可增量擴充套件的雜湊表結構 增量擴充套件體現在當我們這個結構儲存量在擴大的時候會它也會逐漸的擴大它的記憶體結構用雜湊表來實現也是為了提高查詢效率

  • 區域性性原理的最佳應用 例如我們在呼叫方法的時候,其實每次呼叫的都是某幾個方法,如果把這些呼叫頻率較高的方法放到方法快取中,那麼下次呼叫的命中就會更高

WX20181211-160428@2x.png

  • cache_t其實可以理解為一個陣列來實現的,陣列當中都是bucket_t這樣的一個結構體來封裝的

  • 對於bucket_t它有兩個重要的成員變數,一個是key,一個是IMP,對於key實際上是OC語言中的selector,在呼叫一個方法的時候它實際上是一個選擇器SEL,對應的就是這裡面的key,我們可以通過一個方法選擇器的名稱來尋找一個方法的具體實現。IMP我們可以理解為一個無型別的函式指標

  • 比如說現在我們拿到一個key,我們可以通過雜湊查詢演算法來定位當前這個key所對應的bucket_t這個結構體位於陣列中的哪個位置,我們定位到這個位置後我們就可以提取其中的IMP指標指向的具體函式實現來呼叫這個函式

class_data_bits_t

  • class_data_bits_t主要是對class_rw_t的封裝

  • class_rw_t代表了類相關的讀寫資訊(比如說我們給類新增的分類中的一些方法,或者屬性以及協議)、對class_ro_t的封裝

  • class_ro_t代表了類相關的只讀資訊

class_rw_t

WX20181211-162348@2x.png

  • class_rw_t主要包含class_ro_t、protocols(協議)、properties(屬性)、methods(方法列表),對於協議,屬性和方法列表的資料結構是二維陣列

class_ro_t

WX20181211-163241@2x.png

  • class_ro_t主要包含name(名字)、ivars(成員變數)、protocols(協議)、properties(屬性)、methodList(方法列表),對於成員變數、協議、屬性和方法列表的資料結構是一維陣列,與class_rw_t的區別在:在class_rw_t中一般存放的是分類的一些內容,而在class_ro_t中一般存放類原始的一些內容和資訊(個人理解)

method_t

WX20181211-164853@2x.png

  • method_t是一個結構體型別,主要包括SEL型別的name(方法的名稱)、types(對應的是函式的返回值和引數的組合)、IMP型別的imp(無型別的函式指標,對應函式體)

Type Encodings

WX20181211-165508@2x.png

  • const char* types是一個不可變得字元指標,它的組成結構如上圖所示,首先第一位是返回值,後面是引數,**為啥第一位置是返回值呢?**因為我們定義一個函式的時候,函式的引數可以有多個,但是返回值只能有一個,如果沒有返回值,那麼返回值的型別可以用void來修飾,所以返回值佔據types中的第一個位置

  • 舉個栗子,例如在OC中- (void)aMethod;這個方法,它的返回值是void型別的,沒有引數,它對應的types就是v@:

  • 首先v對應的事返回值(void型別),@對應的是引數1(id型別),:對應的事引數2(SEL),這個解釋一下:當我們呼叫一個方法或者說是方法訊息傳遞時到runtime層面會轉化成呼叫objc_msgSend方法,這個函式中的第一個引數和第二個引數是不可變的(固定的),第一個引數必須是id型別的,及訊息的接受者(self),第二個參數列示是一個選擇器,所以用這樣一個字串來表示

整體資料結構

WX20181211-172050@2x.png

相關文章