官术网_书友最值得收藏!

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,這樣能夠保障兩個參數在交換后對于后續的處理在類型上是安全的。

主站蜘蛛池模板: 平远县| 松江区| 钟祥市| 长汀县| 筠连县| 阳原县| 连州市| 门头沟区| 富民县| 珲春市| 梁平县| 焦作市| 康马县| 深州市| 宁德市| 石棉县| 马山县| 丹凤县| 历史| 页游| 东乡县| 龙山县| 达州市| 江陵县| 绥德县| 桑植县| 南京市| 河东区| 韶关市| 菏泽市| 阜阳市| 登封市| 辽阳市| 武威市| 施甸县| 泾源县| 蒲江县| 栾川县| 育儿| 宣武区| 浦县|