Swift 提供了泛型讓你寫出靈活且可重用的函數(shù)和類型。Swift 標(biāo)準(zhǔn)庫是通過泛型代碼構(gòu)建出來的。Swift 的數(shù)組和字典類型都是泛型集。你可以創(chuàng)建一個Int數(shù)組,也可創(chuàng)建一個String數(shù)組,或者甚至于可以是任何其他 Swift 的類型數(shù)據(jù)數(shù)組。以下實(shí)例是一個非泛型函數(shù) exchange 用來交換兩個 Int 值:實(shí)例// 定義一個交換兩個變量的函數(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í)例只試用與交換整數(shù) Int 類型的變量。如果你想要交換兩個 String 值或者 Double 值,就得重新寫個對應(yīng)的函數(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}從以上代碼來看,它們功能代碼是相同的,只是類型上不一樣,這時我們可以使用泛型,從而避免重復(fù)編寫代碼。泛型使用了占位類型名(在這里用字母 T 來表示)來代替實(shí)際類型名(例如 Int、String 或 Double)。func swapTwoValues(_ a: inout T, _ b: inout T)swapTwoValues 后面跟著占位類型名(T),并用尖括號括起來()。這個尖括號告訴 Swift 那個 T 是 swapTwoValues(_:_:) 函數(shù)定義內(nèi)的一個占位類型名,因此 Swift 不會去查找名為 T 的實(shí)際類型。以下實(shí)例是一個泛型函數(shù) exchange 用來交換兩個 Int 和 String 值:實(shí)例// 定義一個交換兩個變量的函數(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)在棧又只有三個值了。以下實(shí)例是一個非泛型版本的棧,以 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(),用來向棧中壓入值以及從棧中移除值。這些方法被標(biāo)記為 mutating,因?yàn)樗鼈冃枰薷慕Y(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);實(shí)例執(zhí)行結(jié)果為:字符串元素入棧: ["google", "runoob"]出棧元素: runoob整數(shù)元素入棧: [1, 2]Stack 基本上和 IntStack 相同,占位類型參數(shù) Element 代替了實(shí)際的 Int 類型。以上實(shí)例中 Element 在如下三個地方被用作占位符:創(chuàng)建 items 屬性,使用 Element 類型的空數(shù)組對其進(jìn)行初始化。指定 push(_:) 方法的唯一參數(shù) item 的類型必須是 Element 類型。指定 pop() 方法的返回值類型必須是 Element 類型。擴(kuò)展泛型類型當(dāng)你擴(kuò)展一個泛型類型的時候(使用 extension 關(guān)鍵字),你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表。更加方便的是,原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展里是可以使用的,并且這些來自原始類型中的參數(shù)名稱會被用作原始定義中類型參數(shù)的引用。下面的例子擴(kuò)展了泛型類型 Stack,為其添加了一個名為 topItem 的只讀計算型屬性,它將會返回當(dāng)前棧頂端的元素而不會將其從棧中移除:泛型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)實(shí)例中 topItem 屬性會返回一個 Element 類型的可選值。當(dāng)棧為空的時候,topItem 會返回 nil;當(dāng)棧不為空的時候,topItem 會返回 items 數(shù)組中的最后一個元素。以上程序執(zhí)行輸出結(jié)果為:字符串元素入棧: 棧中的頂部元素是:runoob.["google", "runoob"]我們也可以通過擴(kuò)展一個存在的類型來指定關(guān)聯(lián)類型。例如 Swift 的 Array 類型已經(jīng)提供 append(_:) 方法,一個 count 屬性,以及一個接受 Int 類型索引值的下標(biāo)用以檢索其元素。這三個功能都符合 Container 協(xié)議的要求,所以你只需簡單地聲明 Array 采納該協(xié)議就可以擴(kuò)展 Array。以下實(shí)例創(chuàng)建一個空擴(kuò)展即可:extension Array: Container {}類型約束類型約束指定了一個必須繼承自指定類的類型參數(shù),或者遵循一個特定的協(xié)議或協(xié)議構(gòu)成。類型約束語法你可以寫一個在一個類型參數(shù)名后面的類型約束,通過冒號分割,來作為類型參數(shù)鏈的一部分。這種作用于泛型函數(shù)的類型約束的基礎(chǔ)語法如下所示(和泛型類型的語法相同):func someFunction(someT: T, someU: U) {? ? // 這里是泛型函數(shù)的函數(shù)體部分}上面這個函數(shù)有兩個類型參數(shù)。第一個類型參數(shù) T,有一個要求 T 必須是 SomeClass 子類的類型約束;第二個類型參數(shù) U,有一個要求 U 必須符合 SomeProtocol 協(xié)議的類型約束。實(shí)例泛型// 非泛型函數(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)")}索引下標(biāo)從 0 開始。以上程序執(zhí)行輸出結(jié)果為:runoob 的索引為 3關(guān)聯(lián)類Swift 中使用 associatedtype 關(guān)鍵字來設(shè)置關(guān)聯(lián)類型實(shí)例。下面例子定義了一個 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 的下標(biāo)檢索到容器中的每一個元素? ? subscript(i: Int) -> ItemType { get }}// Stack 結(jié)構(gòu)體遵從 Container 協(xié)議struct Stack: Container {? ? // Stack的原始實(shí)現(xiàn)部分? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }? ? // Container 協(xié)議的實(shí)現(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)系。實(shí)例下面的例子定義了一個名為allItemsMatch的泛型函數(shù),用來檢查兩個Container實(shí)例是否包含相同順序的相同元素。如果所有的元素能夠匹配,那么返回 true,反之則返回 false。泛型// Container 協(xié)議protocol Container {? ? associatedtype ItemType? ? // 添加一個新元素到容器里? ? mutating func append(_ item: ItemType)? ? // 獲取容器中元素的數(shù)? ? var count: Int { get }? ? // 通過索引值類型為 Int 的下標(biāo)檢索到容器中的每一個元素? ? subscript(i: Int) -> ItemType { get }} // // 遵循Container協(xié)議的泛型TOS類型struct Stack: Container {? ? // Stack的原始實(shí)現(xiàn)部分? ? var items = [Element]()? ? mutating func push(_ item: Element) {? ? ? ? items.append(item)? ? }? ? mutating func pop() -> Element {? ? ? ? return items.removeLast()? ? }? ? // Container 協(xié)議的實(shí)現(xiàn)部分? ? mutating func append(_ item: Element) {? ? ? ? self.push(item)? ? }? ? var count: Int {? ? ? ? return items.count? ? }? ? subscript(i: Int) -> Element {? ? ? ? return items[i]? ? }}// 擴(kuò)展,將 Array 當(dāng)作 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é)果為:
匹配所有元素