[筆記] Swift深入淺出常見語法 – Static vs Dynamic Dispatch

man in white dress shirt beside woman in white dress shirt
分享

Dispatch Method的背景知識,已經有點跳脫寫程式的範疇,所以改以機器的角度來思考,會比較好理解。開始之前,我們先記著一個原則:函式 (function) 會被記在記憶體中,同時會被派發一個位址(Address或指針 Pointer),我們稱為「函式位址」,而派發位址來呼叫函式的方法,就叫做Dispatch Method。其中分為靜態派發(Static)與動態派發(Dynamic)。

  • 靜態:直接給定函式位址
  • 動態:透過其他資訊取得函式位址

比喻

今天小S跟大D兩位工程師,收到Matchbox總公司的Email,指派要到「同一間子公司的同一個部門」出差開會,而他們的 Email內容包含的資訊有,小S:子公司名稱、子公司地址、部門樓層位置。大D:只收到子公司的名稱與部門名稱。請問誰最能輕鬆抵達會議現場?顯然是小S。大D要先查到子公司的地址、再問接待人員部門在哪個樓層。

靜態派發,就類似小S收到的Email內容,CPU會在compile-time就提供精準的函式位址。而動態派發,則需要在runtime時,透過額外的流程,才能拿到函式位址。由此可知 => Static 效率 > Dynamic 效率

Dynamic Dispatch

動態派發一般只發生在Reference Type(如 Class),主要原因是「Inheritance 繼承」。Value Types並不支持繼承。另外還有在實現「Polymorphism 多型」時,也會透過動態派發。動態派發有以下兩種類型:

Table Dispatch

此類型會運用一個Table (witness table or virtual table),代表著某個Class的函式位址陣列。每個Class會有自己的一個表,如果是繼承父類的Subclass,也會包含Overridden與自己的functions。

function 1 0x121function 2 0x222function 3 0x322
overridden function 1function 2function 3
Subclass Address 0xC00

Compiler在Runtime時,會先讀取特定類的表以取得函式位址,再跳轉呼叫函式。因此額外執行了兩次讀與跳轉,加上執行在runtime,編譯器無法對其進行優化,這也是效率會比靜態來得差的原因。

要呼叫function 2的整個流程就是:讀取物件 0xC00 的函式表 -> 讀取取得 function 2的位址 (0x222) -> 跳轉至 0x222 執行

Message Dispatch

Message也應用了Table,但會以Tree的方式來呈現不同層級關係。

Swift中的派發方式

Initial
Declaration
Extension
Value Type (Struct)StaticStatic
ProtocolTableStatic
ClassTableStatic
NSObject
Subclass
TableMessage
初始化與Extension的派發方式

final-> Static
dynamic-> Message
@objc-> Message (Modify Objective-C Visibility)
@inline-> Code generation hint for direct dispatch
使用Keyword改變派發方式

範例

// 1. Value Type (Struct): All Static Dispatch
struct Person {
    func isHungry() -> Bool { } // Static
}
extension Person {
    func sayHello() -> String { } // Static
}

// 2. Protocol: Table & Static
protocol Animal {
    func isCute() -> Bool { } // Table
}
extension Animal {
    func canGetAngry() -> Bool { } // Static
}

// 3. Class
class Dog: Animal {
    func isCute() -> Bool { } // Table
    // add @objc & dynamic keyword
    @objc dynamic func hoursSleep() -> Int { } // Table -> Message
}
extension Dog {
    func canBite() -> Bool { } // Static
    // add @objc keyword
    @objc func goWild() { } // Static -> Message
}
// add final keyword
final class Employee {
    func canCode() -> Bool { } // Table -> Static 
}

參考資料:

  1. Static vs Dynamic Dispatch in Swift: A decisive choice
  2. Static Dispatch Over Dynamic Dispatch
  3. Method Dispatch in Swift | Static | Dynamic | Message
  4. Static and Dynamic Dispatch

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *