Swift進階雜談4:方法調度

經過以前的分析,結構體是值類型,類是引用類型。那結構體和類的方法存儲在哪裏?咱們分析一下swift

靜態派發

值類型對象的函數的調用方式是靜態調用,即直接地址調用,調用函數指針,這個函數指針在編譯、連接完成後,當前函數的地址就已經肯定了,拿在執行代碼的過程當中就直接跳轉到這個地址來執行當前對應的方法,存放在代碼段,而結構體內部並不存放方法。所以能夠直接經過地址直接調用數組

動態派發

類中聲明的方法是經過V-table來進行調度的。 V-TableSIL中的表示是這樣的:安全

//聲明sil vtable關鍵字
1 decl ::= sil-vtable
//sil vtable中包含 關鍵字、標識(即類名)、全部的方法
2 sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
//方法中包含了聲明以及函數名稱
3 sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-na
me
複製代碼

咱們經過一個簡單的例子來看一下markdown

class LGTeacher {
    func teach(){}
    func teach2(){}
    func teach3(){}
    func teach4(){}
    @objc deinit{}
    init(){}
}
複製代碼

經過SIL源文件查看其在SIL中的v-table,以下圖所示 ide

由上圖可知函數

  • sil_vtable:關鍵字
  • LGTeacher:代表當前是LGTeacher class的函數表
  • 其次就是當前方法的生命對應着方法的名稱
  • 本質:函數表的本質就相似於咱們理解的數組,聲明在class內部的方法在不加任何關鍵字修飾的過程當中,連續存放在咱們當前的地址空間

經過查看源碼發現其內部是經過for循環編碼,而後offset+index偏移,而後獲取method,將其存入到偏移後的內存中,從這裏能夠印證函數是連續存放的。編碼

能夠得出結論:對於class中函數來講,類的方法調度是經過V-Taable,其本質就是一個連續的內存空間(數組結構)spa

函數聲明位置的不一樣也會致使派發方式的不一樣。若是咱們在類的擴展中聲明的函數,這裏就是一個直接調用。

其緣由是由於子類將父類的函數表所有繼承了,若是此時子類增長函數,會繼續在連續的地址中插入,假設extension函數也在函數表中,則意味着子類也有,可是子類沒法並無相關的指針記錄函數是父類方法仍是子類方法,因此不知道方法該從哪裏插入,致使extension中的函數沒法安全的放入子類中。因此在這裏能夠側面證實extension中的方法是直接調用的,且只屬於類,子類是沒法繼承的指針

開發注意點:

  • 繼承方法和屬性,不能寫extension中。
  • 而extension中建立的函數,必定是隻屬於本身類,可是其子類也有其訪問權限,只是不能繼承和重寫

擴展 : final、@objc、dynamic修飾函數

final修飾code

  • final修飾的方法是直接調度

@objc修飾

  • 使用@objc關鍵字是將swift中的方法暴露給OC,@objc修飾的方法是函數表調度
  • 若是隻是經過@objc修飾函數,OC仍是沒法調用swift方法的,所以若是想要OC訪問swift,class須要繼承NSObject
  • 查看SIL文件發現被@objc修飾的函數聲明有兩個:swift+OC,即在SIL文件中生成了兩個方法
    • swift原有的函數
    • @objc標記暴露給OC來使用的函數:內部調用swift的

dynamic修飾 使用dynamic的意思是能夠動態修改,覺得着當繼承自NSObject時,可使用method-swizzling

場景:swift中實現方法交換 在swift中的須要交換的函數前,使用dynamic修飾,而後經過:@_dynamicReplacement(for: 函數符號)進行交換,以下所示

class PDTeacher: NSObject {
    dynamic func teach(){ print("teach") }
    func teach2(){ print("teach2") }
    func teach3(){ print("teach3") }
    func teach4(){ print("teach4") }
    @objc deinit{}
    override init(){}
}

extension PDTeacher{
    @_dynamicReplacement(for: teach)
    func teach5(){
        print("teach5")
    }
}
複製代碼

teach方法替換成了teach5

總結

  • struct值類型,其中函數的調度屬於直接調用地址,即靜態調度
  • class引用類型,其中函數的調度是經過V-Table函數表來進行調度的,即動態調度
  • extension中的函數調度方式是直接調度
  • final修飾的函數調度方式是直接調度
  • @objc隨時的函數調度方式是函數表調度,若是OC中須要使用,class還必須繼承NSObject
  • dynamic修飾的函數的調度方式是函數表調度,是函數具備動態性。
  • @objc + dynamic組合修飾的函數調度,是執行的是objc_msgSend流程,即動態消息轉發
相關文章
相關標籤/搜索