[swift 進階]讀書筆記-第九章:泛型 C9P4 泛型的工作方式 How Generics Work
本小節講了一些關於泛型底層
的知識點和一些特殊情況下
的使用。可以當做擴充知識面去學習。
我們先看看一個最簡單的泛型函式的底層實現
func min<T: Comparable>(_ x: T, _ y: T) -> T { returny<x?y:x } 複製程式碼
編譯器無法為這個函式直接生產程式碼
,原因如下:
1.編譯器不知道T的```變數大小``` 2.編譯器不知道需要呼叫的<函式是否有過載,所以也不知道需要呼叫的```函式的地址``` 複製程式碼
swift 為函式引入了一套間接的中間層
來解決這個問題,引入了一個容器,編譯器會把泛型放到這個容器中
對於泛型的引數,編譯器還維護了一個或者多個目擊表(witness table)
,包括一個值目擊表
,以及每個協議約束的協議目擊表
。 這些表將執行時的函式動態派發
到正確的實現中。 表裡實際上放的都是指標
。同時還記錄了型別大小和對齊方式
。
泛型特化
光看泛型特化這個詞很難理解它的用法和意思,後面我們會講。
使用泛型特化的原因:
大家先了解一個swift的設計目標:"編譯一次,動態派發"
swift 泛型API只需要知道泛型函式或者型別的宣告,並不關心實現
。
所以結果的程式碼不是那麼直接。這會導致執行時效能低
swift庫中有很多泛型的使用,效能開銷很容易疊加。
這裡就引入了泛型特化(generic specialization)
來避免這個不必要的開銷。
泛型特化的本質:
編譯器按照具體的引數型別
將函式進行複製
。
文章一開始的例子中,可能針對於Int的引數型別特化出一個方法是這樣的
func min(_ x: Int, _ y: Int) -> Int { returny<x?y:x } 複製程式碼
泛型特化不僅能去掉虛擬派發的開銷
, 還可以讓內聯
等進一步優化成為可能
泛型特化的決定在編譯時
進行。特化的引數型別很可能是你經常使用
的具體型別,
如果你經常使用Int 只用過一次float那麼就會特化出上面的函式。
缺點:泛型定義和呼叫在同一個檔案中
時,泛型特化才能工作,只能在模組內使用:sweat_smile:(標準庫中的泛型方法除外,因為標準庫的定義對於所有模組都是可見的)
全模組優化
因為泛型特化是一個很嚴重的限制,所以編譯器引入了一個標誌來啟用全模組優化
可以通過向 swiftc 傳遞 -whole-module-optimization 來開啟全模組優化
一般都是在釋出版本中
進行這項操作,
優點:
大幅度提升效能缺點:
會帶來更長的編譯時間
@_specialize 關鍵字
@_specialize
是一個非官方
的標籤,計劃將來會引入到標準庫中。
作用:將你的泛型程式碼進行指定版本的特化,使其在其他模組中
也可用。(你必須要指明想要進行特化的型別列表)
使用如下:
@_specialize(exported: true, where T == Int) @_specialize(exported: true, where T == String) public func min<T: Comparable>(_ x: T, _ y: T) -> T { returny<x?y:x } 複製程式碼
over~