Swift 提供了泛型讓你寫出靈活且可重用的函數和類型坝橡。
Swift 標準庫是通過泛型代碼構建出來的。
Swift 的數組和字典類型都是泛型集。
你可以創(chuàng)建一個Int數組想帅,也可創(chuàng)建一個String數組睁本,或者甚至于可以是任何其他 Swift 的類型數據數組尿庐。
以下實例是一個非泛型函數 exchange 用來交換兩個 Int 值:
// 定義一個交換兩個變量的函數
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交換前數據: \(numb1) 和 \(numb2)")
swapTwoInts(&numb1, &numb2)
print("交換后數據: \(numb1) 和 \(numb2)")
以上實例只試用與交換整數 Int 類型的變量。如果你想要交換兩個 String 值或者 Double 值呢堰,就得重新寫個對應的函數抄瑟,例如 swapTwoStrings(::) 和 swapTwoDoubles(::)
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<T>(_ a: inout T, _ b: inout T)
swapTwoValues 后面跟著占位類型名(T),并用尖括號括起來(<T>)布轿。這個尖括號告訴 Swift 那個 T 是 swapTwoValues(::) 函數定義內的一個占位類型名哮笆,因此 Swift 不會去查找名為 T 的實際類型。
以下實例是一個泛型函數 exchange 用來交換兩個 Int 和 String 值:
// 定義一個交換兩個變量的函數
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交換前數據: \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交換后數據: \(numb1) 和 \(numb2)")
var str1 = "A"
var str2 = "B"
print("交換前數據: \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交換后數據: \(str1) 和 \(str2)")
注意:不同類型可不能互相交換哦(如一個string數據和一個int數據進行交換汰扭,是會報錯的)
泛型類型
Swift 允許你定義你自己的泛型類型稠肘。
自定義類、結構體和枚舉作用于任何類型萝毛,如同 Array 和 Dictionary 的用法项阴。
以下實例是一個非泛型版本的棧,以 Int 型的棧為例:
struct IntStack {
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
這個結構體在棧中使用一個名為 items 的 Array 屬性來存儲值笆包。Stack 提供了兩個方法:push(_:) 和 pop()环揽,用來向棧中壓入值以及從棧中移除值。這些方法被標記為 mutating庵佣,因為它們需要修改結構體的 items 數組歉胶。
上面的 IntStack 結構體只能用于 Int 類型。不過巴粪,可以定義一個泛型 Stack 結構體通今,從而能夠處理任意類型的值。
下面是相同代碼的泛型版本:
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>()
print("字符串元素入棧: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
print(stackOfStrings.items);
let deletetos = stackOfStrings.pop()
print("出棧元素: " + deletetos)
var stackOfInts = Stack<Int>()
print("整數元素入棧: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);
Stack 基本上和 IntStack 相同肛根,占位類型參數 Element 代替了實際的 Int 類型辫塌。
以上實例中 Element 在如下三個地方被用作占位符:
1.創(chuàng)建 items 屬性,使用 Element 類型的空數組對其進行初始化派哲。
2.指定 push(_:) 方法的唯一參數 item 的類型必須是 Element 類型臼氨。
3.指定 pop() 方法的返回值類型必須是 Element 類型。
擴展泛型類型
當你擴展一個泛型類型的時候(使用 extension 關鍵字)芭届,你并不需要在擴展的定義中提供類型參數列表储矩。更加方便的是,原始類型定義中聲明的類型參數列表在擴展里是可以使用的喉脖,并且這些來自原始類型中的參數名稱會被用作原始定義中類型參數的引用椰苟。
//下面的例子擴展了泛型類型 Stack抑月,為其添加了一個名為 topItem 的只讀計算型屬性树叽,它將會返回當前棧頂端的元素而不會將其從棧中移除:
struct Stack<Element> {
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<String>()
print("字符串元素入棧: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
if let topItem = stackOfStrings.topItem {
print("棧中的頂部元素是:\(topItem).")
}
print(stackOfStrings.items)
//實例中 topItem 屬性會返回一個 Element 類型的可選值。當棧為空的時候谦絮,topItem 會返回 nil题诵;當棧不為空的時候洁仗,topItem 會返回 items 數組中的最后一個元素。
我們也可以通過擴展一個存在的類型來指定關聯(lián)類型性锭。
例如 Swift 的 Array 類型已經提供 append(_:) 方法赠潦,一個 count 屬性,以及一個接受 Int 類型索引值的下標用以檢索其元素草冈。這三個功能都符合 Container 協(xié)議的要求她奥,所以你只需簡單地聲明 Array 采納該協(xié)議就可以擴展 Array。
以下實例創(chuàng)建一個空擴展即可:
extension Array: Container {}
類型約束
類型約束指定了一個必須繼承自指定類的類型參數怎棱,或者遵循一個特定的協(xié)議或協(xié)議構成哩俭。
類型約束語法:
你可以寫一個在一個類型參數名后面的類型約束,通過冒號分割拳恋,來作為類型參數鏈的一部分凡资。這種作用于泛型函數的類型約束的基礎語法如下所示(和泛型類型的語法相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 這里是泛型函數的函數體部分
}
上面這個函數有兩個類型參數。第一個類型參數 T谬运,有一個要求 T 必須是 SomeClass 子類的類型約束隙赁;第二個類型參數 U,有一個要求 U 必須符合 SomeProtocol 協(xié)議的類型約束梆暖。
// 非泛型函數伞访,查找指定字符串在數組中的索引
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í)行輸出結果為:
runoob 的索引為 3
關聯(lián)類
Swift 中使用 associatedtype 關鍵字來設置關聯(lián)類型實例轰驳。
下面例子定義了一個 Container 協(xié)議咐扭,該協(xié)議定義了一個關聯(lián)類型 ItemType。
Container 協(xié)議只指定了三個任何遵從 Container 協(xié)議的類型必須提供的功能滑废。遵從協(xié)議的類型在滿足這三個條件的情況下也可以提供其他額外的功能蝗肪。
// Container 協(xié)議
protocol Container {
associatedtype ItemType
// 添加一個新元素到容器里
mutating func append(_ item: ItemType)
// 獲取容器中元素的數
var count: Int { get }
// 通過索引值類型為 Int 的下標檢索到容器中的每一個元素
subscript(i: Int) -> ItemType { get }
}
// Stack 結構體遵從 Container 協(xié)議
struct Stack<Element>: Container {
// Stack<Element> 的原始實現(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<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素個數
print( tos.count)
Where 語句
類型約束能夠確保類型符合泛型函數或類的定義約束。
你可以在參數列表中通過where語句定義參數的約束蠕趁。
你可以寫一個where語句薛闪,緊跟在在類型參數列表后面,where語句后跟一個或者多個針對關聯(lián)類型的約束俺陋,以及(或)一個或多個類型和關聯(lián)類型間的等價(equality)關系豁延。
//下面的例子定義了一個名為allItemsMatch的泛型函數,用來檢查兩個Container實例是否包含相同順序的相同元素腊状。
//如果所有的元素能夠匹配诱咏,那么返回 true,反之則返回 false缴挖。
// Container 協(xié)議
protocol Container {
associatedtype ItemType
// 添加一個新元素到容器里
mutating func append(_ item: ItemType)
// 獲取容器中元素的數
var count: Int { get }
// 通過索引值類型為 Int 的下標檢索到容器中的每一個元素
subscript(i: Int) -> ItemType { get }
}
// // 遵循Container協(xié)議的泛型TOS類型
struct Stack<Element>: Container {
// Stack<Element> 的原始實現(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<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 檢查兩個容器含有相同數量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 檢查每一對元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
var aos = ["google", "runoob", "taobao"]
if allItemsMatch(tos, aos) {
print("匹配所有元素")
} else {
print("元素不匹配")
}
ps:語法總結
1:基本語法
func someFunction<T>(someParamA: T, someParamB: T)
2:類型約束
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 這里是泛型函數的函數體部分
}