ObjC Runtime簡析

ObjC是一門動態性比較強的編程語言,而這一系列的動態性就是依賴於Runtime來支撐的。算法

isa指針

在iPhone cpu 使用arm64架構以前,isa指針就是一個Class類型的普通指針,存儲着Class、Meta-Class的內存地址。在這以後apple對isa指針進行了優化,利用位域的技術,將isa指針換成了一個union(共用體)。arm64架構以後的isa指針每一位都存儲着不一樣的信息,在isa的union中有一個結構體表示了每一位對應的含義。編程

isa指針

Class

在runtime源碼中class的定義爲:api

其中咱們能夠看到經過bits的data()函數能夠獲取到一個class_rw_t的結構體指針。class_rw_t結構體中存放着咱們熟悉的東西:方法列表、屬性列表和協議列表等。除此以外還有一個class_ro_t的結構體指針,它指向的結構體擁有與class_rw_t結構體相似的結構,也存放有方法列表、協議列表,初次以外還有成員變量列表等數據。數組

經過class_rw_tclass_ro_t的名字咱們能夠看出,rw表明的是可讀可寫的結構,而ro則表明只讀的結構。緩存

整體上說class的結構能夠用下圖表示架構

class_ro_t

class_rw_t中方法列表、屬性列表、協議列表都是xxx_array_t二維數組,而class_ro_t中的方法列表,協議列表等都是xxx_list_t的一維數組。class_rw_t中的二維數組中的存放的就是許多xxx_list_t的一維數組。這些一維數組多是來自原始類(class_ro_t中存放的是類的原始的信息),也多是來自category。app

method_t

對於methodlist而言,一個方法就是method_list_t的一個元素,其關係能夠用下圖表示:編程語言

method_t存放的位置

也就是說method_t才方法列表中存儲的最小元素。method_t的是一個結構,它是對方法/函數的封裝。函數

method_t

method_t有方法名稱(name)、返回值類型和參數類型的編碼(types)、指向函數的指針(imp)。優化

name

name 就是方法的名稱,它是SEL類型的。SEL就是selector是方法選擇器,它是根據方法名字生成的,本質上就是一個字符串,跟方法存放的位置無關。也就是說不一樣類中相同名字的方法的方法選擇器是同樣的。

獲取SEL的方法可使用@selector()或者runtime api中的sel_registerName()得到。使用sel_getName()或者NSStringFromSelector()將其轉換爲字符串。

types

types 包含了函數返回值和參數編碼的字符串。它就是一個字符串,將函數返回值類型和參數類型按照必定的編碼規則生成的一個字符串。可使用@encode()將具體類型表示成字符串編碼。

apple提供的編碼規則以下:

types的編碼規則

imp

imp在runtime中的定義以下:

正如其註釋所說:這是一個指向方法實現的函數指針。

方法緩存

咱們知道平時咱們進行方法調用的時候不可能一直按照從isa指針開查找方法的規則來查找方法的實現的。runtime定義的類中有一個方法緩存,這裏面緩存了調用過的方法。當咱們調用方法的時候runtime會首先從緩存中查找有沒有方法的實現被緩存過。如如有,則直接經過緩存取出方法進行調用,若是沒有則再按照isa指針的流程進行方法實現的查找。

方法緩存

方法緩存是使用散列表實現的。SEL做爲散列表的key,IMP和SEL做爲散列表的value。

經過上圖咱們也能夠看出_buckets是一個數組,數組中存放了方法緩存的散列表。那麼是如何從方法緩存中取出方法來的呢?若是是遍歷查找的話,效率是很是低的,apple利用了一種算法,它利用@selector(method_name)和mask(buckets的長度減一)進行一次按位與運算得出一個下標,而後將方法緩存存放到這個下標對應的位置,一樣取緩存的時候也是進行了這樣一次操做。這樣一來效率就提升了,可是整個數組中會出現一些沒有用到的空位置,因此這是一種利用空間來換時間的算法。

查找緩存

END

本篇簡單的介紹了Runtime中關於isa、class、以及方法緩存的相關問題。下篇介紹obj方法調用的相關問題。

相關文章
相關標籤/搜索