- 跟戴銘學iOS編程:理順核心知識點
- 戴銘
- 1045字
- 2024-01-19 15:19:45
1.2 泛型
泛型的概念最早出自C++的模板,Swift的泛型和C++模板設計的思路是一致的。為什么不與Java的泛型一致呢?原因是C++是一種編譯時多態技術,而Java是運行時多態技術。Java運行時多態是在運行時才能確定的,所以會有運行時額外的計算,缺點是通過類型擦除生成的代碼只有一份。而C++在編譯時通過編譯器來確定類型,所以在運行時就不需要額外計算了,這樣效率會高一些,缺點是生成的機器碼的二進制包會大一些,雖然執行快但可能會有更多的I/O。Swift采用編譯時多態技術一方面是和C++一樣在I/O性能和執行效率中選擇了執行效率,另一方面是為了讓代碼更加安全。當Java在類型擦除中進行向下轉型時丟失的類型只有在運行時才能看到。而Swift提供了額外的類型約束,使得泛型在定義的時候就能夠判定類型。
使用泛型編寫代碼可以讓我們寫出更簡潔、安全的代碼。類型轉換的代碼也可以由編譯器完成,減少了手寫類型強制轉換帶來的運行時出錯的問題。隨著編譯器或虛擬機技術的不斷進步,在代碼不變的情況下,優化工作會在編譯層面完成,從而獲得潛在性能方面的收益。綜上所述,泛型可以寫出可重用、支持任意類型的函數,以及靈活、清晰、優雅的代碼。
下面我們用示例分析泛型是如何解決具體問題的。
let dragons = ["red dragon", "white dragon", "blue dragon"] func showDragons(dragons : [String]) { for dragon in dragons { print("\(dragon)") } } showDragons(dragons: dragons)
如果用數字類型作為編號來代表每只龍,并且需要一個函數來顯示出它們的數字編號,那么需要再寫一個函數,如下所示。
let dragons = [1276, 8737, 1173] func showDragons(dragons : [Int]) { for dragon in dragons { print("\(dragon)") } } showDragons(dragons: dragons)
以上兩個函數里的內容基本一樣,只是參數的類型不同,此時就是使用泛型的最佳時刻。在Swift中,泛型能夠在function、class、struct、enums、protocol和extension中使用。首先看一下function是怎么使用泛型的,如下所示。
let dragonsId = [1276, 8737, 1173] let dragonsName = ["red dragon", "blue dragon", "black dragon"] func showDragons<T>(dragons : [T]) { for dragon in dragons { print("\(dragon)") } } showDragons(dragons: dragonsName) showDragons(dragons: dragonsId)
上面的類型參數T最好能夠具有一定的描述性,就像字典(Dictionary)定義中的key和value,以及數組(Array)里的element一樣具有描述性。Swift的基礎庫大量使用泛型,例如數組和字典都是泛型集合。我們既可以創建Swift支持的任意類型的數組,也可以創建存儲任意類型值的字典。下面我們看一看系統提供的swap方法是如何運用泛型的,代碼在Swift的源代碼路徑stdlib/public/core/MutableCollection.swift里,如下所示。
// the legacy swap free function // /// Exchanges the values of the two arguments. /// /// The two arguments must not alias each other. To swap two elements of /// a mutable collection, use the 'swapAt(_:_:)' method of that collection /// instead of this function. /// /// - Parameters: /// - a: The first value to swap. /// - b: The second value to swap. @inlinable public func swap<T>(_ a: inout T, _ b: inout T) { // Semantically equivalent to (a,b) = (b,a). // Microoptimized to avoid retain/release traffic. let p1 = Builtin.addressof(&a) let p2 = Builtin.addressof(&b) _debugPrecondition( p1 != p2, "swapping a location with itself is not supported") // Take from P1. let tmp: T = Builtin.take(p1) // Transfer P2 into P1. Builtin.initialize(Builtin.take(p2) as T, p1) // Initialize P2. Builtin.initialize(tmp, p2) }
這里的兩個參數a和b使用了同樣的泛型T,這樣能夠保障兩個參數在交換后對于后續的處理在類型上是安全的。
- 極簡算法史:從數學到機器的故事
- FreeSWITCH 1.8
- Oracle Database In-Memory(架構與實踐)
- Python計算機視覺編程
- Building Mapping Applications with QGIS
- Effective Python Penetration Testing
- 鋒利的SQL(第2版)
- 微信公眾平臺開發:從零基礎到ThinkPHP5高性能框架實踐
- Unity 5.x By Example
- SQL基礎教程(第2版)
- C語言程序設計
- Learning AngularJS for .NET Developers
- 0 bug:C/C++商用工程之道
- Learning Cocos2d-JS Game Development
- 原型設計:打造成功產品的實用方法及實踐