Swift 提供了泛型讓你寫出靈活且可重用的函數(shù)和類型矫限。Swift 標準庫是通過泛型代碼構(gòu)建出來的催式。Swift 的數(shù)組和字典類型都是泛型集徽曲。你可以創(chuàng)建一個Int數(shù)組临庇,也可創(chuàng)建一個String數(shù)組反璃,或者甚至于可以是任何其他 Swift 的類型數(shù)據(jù)數(shù)組。以下實例是一個非泛型函數(shù) exchange 用來交換兩個 Int 值:實例// 定義一個交換兩個變量的函數(shù)func swapTwoInts(_ a: inout Int, _ b: inout Int) {? ? let temporaryA = a? ? a = b? ? b = temporaryA} var numb1 = 100var numb2 = 200 print("交換前數(shù)據(jù): \(numb1) 和 \(numb2)")swapTwoInts(&numb1, &numb2)print("交換后數(shù)據(jù): \(numb1) 和 \(numb2)")以上程序執(zhí)行輸出結(jié)果為:交換前數(shù)據(jù): 100 和 200交換后數(shù)據(jù): 200 和 100以上實例只試用與交換整數(shù) Int 類型的變量假夺。如果你想要交換兩個 String 值或者 Double 值淮蜈,就得重新寫個對應的函數(shù),例如 swapTwoStrings(_:_:) 和 swapTwoDoubles(_:_:)已卷,如下所示:String 和 Double 值交換函數(shù)func swapTwoStrings(_ a: inout String, _ b: inout String) {? ? let temporaryA = a? ? a = b? ? b = temporaryA} func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {? ? let temporaryA = a? ? a = b? ? b = temporaryA}從以上代碼來看梧田,它們功能代碼是相同的,只是類型上不一樣,這時我們可以使用泛型裁眯,從而避免重復編寫代碼鹉梨。泛型使用了占位類型名(在這里用字母 T 來表示)來代替實際類型名(例如 Int、String 或 Double)穿稳。func swapTwoValues(_ a: inout T, _ b: inout T)swapTwoValues 后面跟著占位類型名(T)存皂,并用尖括號括起來()。這個尖括號告訴 Swift 那個 T 是 swapTwoValues(_:_:) 函數(shù)定義內(nèi)的一個占位類型名逢艘,因此 Swift 不會去查找名為 T 的實際類型旦袋。以下實例是一個泛型函數(shù) exchange 用來交換兩個 Int 和 String 值:實例// 定義一個交換兩個變量的函數(shù)func swapTwoValues(_ a: inout T, _ b: inout T) {? ? let temporaryA = a? ? a = b? ? b = temporaryA} var numb1 = 100var numb2 = 200 print("交換前數(shù)據(jù):? \(numb1) 和 \(numb2)")swapTwoValues(&numb1, &numb2)print("交換后數(shù)據(jù): \(numb1) 和 \(numb2)") var str1 = "A"var str2 = "B" print("交換前數(shù)據(jù):? \(str1) 和 \(str2)")swapTwoValues(&str1, &str2)print("交換后數(shù)據(jù): \(str1) 和 \(str2)")以上程序執(zhí)行輸出結(jié)果為:交換前數(shù)據(jù):? 100 和 200交換后數(shù)據(jù): 200 和 100交換前數(shù)據(jù):? A 和 B交換后數(shù)據(jù): B 和 A泛型類型Swift 允許你定義你自己的泛型類型。自定義類它改、結(jié)構(gòu)體和枚舉作用于任何類型疤孕,如同 Array 和 Dictionary 的用法。接下來我們來編寫一個名為 Stack (棧)的泛型集合類型央拖,棧只允許在集合的末端添加新的元素(稱之為入棧)祭阀,且也只能從末端移除元素(稱之為出棧)。圖片中從左到右解析如下:三個值在棧中爬泥。第四個值被壓入到棧的頂部〖硖郑現(xiàn)在有四個值在棧中崩瓤,最近入棧的那個值在頂部袍啡。棧中最頂部的那個值被移除,或稱之為出棧却桶。移除掉一個值后境输,現(xiàn)在棧又只有三個值了。以下實例是一個非泛型版本的棧颖系,以 Int 型的棧為例:Int 型的棧struct IntStack {? ? var items = [Int]()? ? mutating func push(_ item: Int) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Int {? ? ? ? return items.removeLast()? ? }}這個結(jié)構(gòu)體在棧中使用一個名為 items 的 Array 屬性來存儲值嗅剖。Stack 提供了兩個方法:push(_:) 和 pop(),用來向棧中壓入值以及從棧中移除值嘁扼。這些方法被標記為 mutating信粮,因為它們需要修改結(jié)構(gòu)體的 items 數(shù)組。上面的 IntStack 結(jié)構(gòu)體只能用于 Int 類型趁啸。不過强缘,可以定義一個泛型 Stack 結(jié)構(gòu)體,從而能夠處理任意類型的值不傅。下面是相同代碼的泛型版本:泛型的棧struct Stack{? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }} var stackOfStrings = Stack()print("字符串元素入棧: ")stackOfStrings.push("google")stackOfStrings.push("runoob")print(stackOfStrings.items); let deletetos = stackOfStrings.pop()print("出棧元素: " + deletetos) var stackOfInts = Stack()print("整數(shù)元素入棧: ")stackOfInts.push(1)stackOfInts.push(2)print(stackOfInts.items);實例執(zhí)行結(jié)果為:字符串元素入棧: ["google", "runoob"]出棧元素: runoob整數(shù)元素入棧: [1, 2]Stack 基本上和 IntStack 相同旅掂,占位類型參數(shù) Element 代替了實際的 Int 類型。以上實例中 Element 在如下三個地方被用作占位符:創(chuàng)建 items 屬性访娶,使用 Element 類型的空數(shù)組對其進行初始化商虐。指定 push(_:) 方法的唯一參數(shù) item 的類型必須是 Element 類型。指定 pop() 方法的返回值類型必須是 Element 類型。擴展泛型類型當你擴展一個泛型類型的時候(使用 extension 關(guān)鍵字)秘车,你并不需要在擴展的定義中提供類型參數(shù)列表典勇。更加方便的是,原始類型定義中聲明的類型參數(shù)列表在擴展里是可以使用的叮趴,并且這些來自原始類型中的參數(shù)名稱會被用作原始定義中類型參數(shù)的引用痴柔。下面的例子擴展了泛型類型 Stack,為其添加了一個名為 topItem 的只讀計算型屬性疫向,它將會返回當前棧頂端的元素而不會將其從棧中移除:泛型struct Stack{? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }} extension Stack {? ? var topItem: Element? {? ? ? return items.isEmpty ? nil : items[items.count - 1]? ? }} var stackOfStrings = Stack()print("字符串元素入棧: ")stackOfStrings.push("google")stackOfStrings.push("runoob") if let topItem = stackOfStrings.topItem {? ? print("棧中的頂部元素是:\(topItem).")} print(stackOfStrings.items)實例中 topItem 屬性會返回一個 Element 類型的可選值咳蔚。當棧為空的時候,topItem 會返回 nil搔驼;當棧不為空的時候谈火,topItem 會返回 items 數(shù)組中的最后一個元素。以上程序執(zhí)行輸出結(jié)果為:字符串元素入棧: 棧中的頂部元素是:runoob.["google", "runoob"]我們也可以通過擴展一個存在的類型來指定關(guān)聯(lián)類型舌涨。例如 Swift 的 Array 類型已經(jīng)提供 append(_:) 方法糯耍,一個 count 屬性,以及一個接受 Int 類型索引值的下標用以檢索其元素囊嘉。這三個功能都符合 Container 協(xié)議的要求温技,所以你只需簡單地聲明 Array 采納該協(xié)議就可以擴展 Array。以下實例創(chuàng)建一個空擴展即可:extension Array: Container {}類型約束類型約束指定了一個必須繼承自指定類的類型參數(shù)扭粱,或者遵循一個特定的協(xié)議或協(xié)議構(gòu)成舵鳞。類型約束語法你可以寫一個在一個類型參數(shù)名后面的類型約束,通過冒號分割琢蛤,來作為類型參數(shù)鏈的一部分蜓堕。這種作用于泛型函數(shù)的類型約束的基礎語法如下所示(和泛型類型的語法相同):func someFunction(someT: T, someU: U) {? ? // 這里是泛型函數(shù)的函數(shù)體部分}上面這個函數(shù)有兩個類型參數(shù)。第一個類型參數(shù) T博其,有一個要求 T 必須是 SomeClass 子類的類型約束套才;第二個類型參數(shù) U,有一個要求 U 必須符合 SomeProtocol 協(xié)議的類型約束慕淡。實例泛型// 非泛型函數(shù)背伴,查找指定字符串在數(shù)組中的索引func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {? ? for (index, value) in array.enumerated() {? ? ? ? if value == valueToFind {? ? ? ? ? ? // 找到返回索引值? ? ? ? ? ? return index? ? ? ? }? ? }? ? return nil}? let strings = ["google", "weibo", "taobao", "runoob", "facebook"]if let foundIndex = findIndex(ofString: "runoob", in: strings) {? ? print("runoob 的索引為 \(foundIndex)")}索引下標從 0 開始。以上程序執(zhí)行輸出結(jié)果為:runoob 的索引為 3關(guān)聯(lián)類Swift 中使用 associatedtype 關(guān)鍵字來設置關(guān)聯(lián)類型實例峰髓。下面例子定義了一個 Container 協(xié)議傻寂,該協(xié)議定義了一個關(guān)聯(lián)類型 ItemType。Container 協(xié)議只指定了三個任何遵從 Container 協(xié)議的類型必須提供的功能儿普。遵從協(xié)議的類型在滿足這三個條件的情況下也可以提供其他額外的功能崎逃。// Container 協(xié)議protocol Container {? ? associatedtype ItemType? ? // 添加一個新元素到容器里? ? mutating func append(_ item: ItemType)? ? // 獲取容器中元素的數(shù)? ? var count: Int { get }? ? // 通過索引值類型為 Int 的下標檢索到容器中的每一個元素? ? subscript(i: Int) -> ItemType { get }}// Stack 結(jié)構(gòu)體遵從 Container 協(xié)議struct Stack: Container {? ? // Stack的原始實現(xiàn)部分? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }? ? // Container 協(xié)議的實現(xiàn)部分? ? mutating func append(_ item: Element) {? ? ? ? self.push(item)? ? }? ? var count: Int {? ? ? ? return items.count? ? }? ? subscript(i: Int) -> Element {? ? ? ? return items[i]? ? }}var tos = Stack()tos.push("google")tos.push("runoob")tos.push("taobao")// 元素列表print(tos.items)// 元素個數(shù)print( tos.count)以上程序執(zhí)行輸出結(jié)果為:["google", "runoob", "taobao"]3Where 語句類型約束能夠確保類型符合泛型函數(shù)或類的定義約束。你可以在參數(shù)列表中通過where語句定義參數(shù)的約束眉孩。你可以寫一個where語句个绍,緊跟在在類型參數(shù)列表后面勒葱,where語句后跟一個或者多個針對關(guān)聯(lián)類型的約束,以及(或)一個或多個類型和關(guān)聯(lián)類型間的等價(equality)關(guān)系巴柿。實例下面的例子定義了一個名為allItemsMatch的泛型函數(shù)凛虽,用來檢查兩個Container實例是否包含相同順序的相同元素。如果所有的元素能夠匹配广恢,那么返回 true凯旋,反之則返回 false。泛型// Container 協(xié)議protocol Container {? ? associatedtype ItemType? ? // 添加一個新元素到容器里? ? mutating func append(_ item: ItemType)? ? // 獲取容器中元素的數(shù)? ? var count: Int { get }? ? // 通過索引值類型為 Int 的下標檢索到容器中的每一個元素? ? subscript(i: Int) -> ItemType { get }} // // 遵循Container協(xié)議的泛型TOS類型struct Stack: Container {? ? // Stack的原始實現(xiàn)部分? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }? ? // Container 協(xié)議的實現(xiàn)部分? ? mutating func append(_ item: Element) {? ? ? ? self.push(item)? ? }? ? var count: Int {? ? ? ? return items.count? ? }? ? subscript(i: Int) -> Element {? ? ? ? return items[i]? ? }}// 擴展钉迷,將 Array 當作 Container 來使用extension Array: Container {} func allItemsMatch(_ someContainer: C1, _ anotherContainer: C2) -> Bool? ? where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {? ? ? ? ? ? ? ? // 檢查兩個容器含有相同數(shù)量的元素? ? ? ? if someContainer.count != anotherContainer.count {? ? ? ? ? ? return false? ? ? ? }? ? ? ? ? ? ? ? // 檢查每一對元素是否相等? ? ? ? for i in 0..()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
var aos = ["google", "runoob", "taobao"]
if allItemsMatch(tos, aos) {
print("匹配所有元素")
} else {
print("元素不匹配")
}
以上程序執(zhí)行輸出結(jié)果為:
匹配所有元素