那天和羣裏一我的聊天,他去過頭條京東面試,用的 Swift,問的問題我都沒都聽過,其中就有這個 Swift 派發機制。決定仍是看下這些基礎原理,而後忽然想到一個問題,爲何要寫博客呢,如今的想法是和別人交流的時候能邏輯很清晰的解釋什麼是 派發機制,否則以個人慣性,可能時間過一點就說的迷迷糊糊了。看了幾篇文章,這兩篇說得不錯。git
kemchenj.github.io/2016-12-25-…github
www.jianshu.com/p/cfe7da018…swift
下面文章寫的有點怪,是先帶着結果看問題的。由於這也是我最開始的思路,和其餘博客不太同樣,我並非先遇到問題。這個話題是我想了解因此就直接查的問題,得出結論,而後根據結論分析一些代碼例子,最後一步是提供驗證這些結論是否正確的方法,由於最後一步比較乏味。ide
經過閱讀上面兩篇文章和 SIL 代碼驗證事後,swift 有四種派發方式:函數
第一篇文章中關於這個部分說的比較詳細,大段抄過來不太好。。優化
派發機制總結表(我沒驗證全,用 SIL 驗證了部分,剩餘是看上面的文章的)ui
圖中那個問好是我實踐的和其餘文章說的不同,最下面有說spa
除上圖外的重要點:翻譯
根據以上邏輯,有兩個代碼問題,剛看的時候有點疑惑,如今能夠用上面的東西解釋下
protocol MyProtocol {}
extension MyProtocol {
func testFuncA() {
print("protocol - funcA")
}
}
class Myclass: MyProtocol {
func testFuncA() {
print("class - funcA")
}
}
let x: MyProtocol = Myclass()
x.testFuncA() // protocol - funcA
複製代碼
因爲 x 是協議類型,因此先會去查協議的函數表,而協議裏並無 funcA 的聲明,因此協議函數表裏就不存在這個方法,也不會去根據表查找實現,就走了協議的 extension 的直接派發。
protocol MyProtocol {
func testFuncA()
}
... (和上面同樣)
let x: MyProtocol = Myclass()
x.testFuncA() // class - funcA
複製代碼
因爲協議函數表裏聲明瞭 funcA,因此用協議函數表查找實現,找到了在 Myclass 中的實現,走的函數表派發。
第三個問題 是上述文章提到的 SR-103
protocol Greetable {
func sayHi()
}
extension Greetable {
func sayHi() {
print("Hello")
}
}
class Person: Greetable {}
class LoudPerson: Person {
func sayHi() {
print("HELLO")
}
}
let x: Greetable = LoudPerson()
x.sayHi() // Hello,protocol
複製代碼
這個被肯定是一個bug。按照正常的理解,協議函數表會查找子類的 sayhi 實現,可是實際上只找了遵循協議的那個 person 類,找不到,因此就走了協議 extension 的直接派發。可是三年過去了,這個bug好像並沒被修復。。
Swift Intermediate Language(SIL) 是 Swift 在編譯過程當中的中間產物。如今把這個文件解析成 SIL,上面第二個文章也說了方法,這裏補充說明下吧。
swiftc -emit-silgen xxx.swift -Onone > xxxx.sil
struct TestStruct {
func testFunc1() { print("ss") }
}
let t = TestStruct()
t.testFunc1()
複製代碼
以下圖所示,咱們要分析的是 main 中的代碼,經過搜尋函數名關鍵字能夠看到 %9 那裏是 function_ref
,能夠看出這個是直接派發
如何看是不是派發方式:
function_ref
:直接派發class_method
:虛函數表派發witness_method
:證據表派發(不知道咋翻譯了)objc_method
:消息機制派發