Swift 泛型

Swift 提供了泛型讓你寫出靈活且可重用的函數(shù)和類型。

Swift 標(biāo)準(zhǔn)庫是通過泛型代碼構(gòu)建出來的。

Swift 的數(shù)組和字典類型都是泛型集。

你可以創(chuàng)建一個(gè)Int數(shù)組,也可創(chuàng)建一個(gè)String數(shù)組,或者甚至于可以是任何其他 Swift 的類型數(shù)據(jù)數(shù)組。

以下示例是一個(gè)非泛型函數(shù) exchange 用來交換兩個(gè) Int 值:

在線示例

// 定義一個(gè)交換兩個(gè)變量的函數(shù)
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var 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 類型的變量。如果你想要交換兩個(gè) String 值或者 Double 值,就得重新寫個(gè)對(duì)應(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
}

從以上代碼來看,它們功能代碼是相同的,只是類型上不一樣,這時(shí)我們可以使用泛型,從而避免重復(fù)編寫代碼。

泛型使用了占位類型名(在這里用字母 T 來表示)來代替實(shí)際類型名(例如 Int、String 或 Double)。

func swapTwoValues<T>(_ a: inout T, _ b: inout T)

swapTwoValues 后面跟著占位類型名(T),并用尖括號(hào)括起來(<T>)。這個(gè)尖括號(hào)告訴 Swift 那個(gè) T 是 swapTwoValues(_:_:) 函數(shù)定義內(nèi)的一個(gè)占位類型名,因此 Swift 不會(huì)去查找名為 T 的實(shí)際類型。

以下示例是一個(gè)泛型函數(shù) exchange 用來交換兩個(gè) Int 和 String 值:

在線示例

// 定義一個(gè)交換兩個(gè)變量的函數(shù)
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var 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 的用法。

接下來我們來編寫一個(gè)名為 Stack (棧)的泛型集合類型,棧只允許在集合的末端添加新的元素(稱之為入棧),且也只能從末端移除元素(稱之為出棧)。

圖片中從左到右解析如下:

  • 三個(gè)值在棧中。

  • 第四個(gè)值被壓入到棧的頂部。

  • 現(xiàn)在有四個(gè)值在棧中,最近入棧的那個(gè)值在頂部。

  • 棧中最頂部的那個(gè)值被移除,或稱之為出棧。

  • 移除掉一個(gè)值后,現(xiàn)在棧又只有三個(gè)值了。

以下示例是一個(gè)非泛型版本的棧,以 Int 型的棧為例:

Int 型的棧

struct IntStack {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

這個(gè)結(jié)構(gòu)體在棧中使用一個(gè)名為 items 的 Array 屬性來存儲(chǔ)值。Stack 提供了兩個(gè)方法:push(_:) 和 pop(),用來向棧中壓入值以及從棧中移除值。這些方法被標(biāo)記為 mutating,因?yàn)樗鼈冃枰薷慕Y(jié)構(gòu)體的 items 數(shù)組。

上面的 IntStack 結(jié)構(gòu)體只能用于 Int 類型。不過,可以定義一個(gè)泛型 Stack 結(jié)構(gòu)體,從而能夠處理任意類型的值。

下面是相同代碼的泛型版本:

泛型的棧

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("nhooo")
print(stackOfStrings.items);
 
let deletetos = stackOfStrings.pop()
print("出棧元素: " + deletetos)
 
var stackOfInts = Stack<Int>()
print("整數(shù)元素入棧: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);

示例執(zhí)行結(jié)果為:

字符串元素入棧: 
["google", "nhooo"]
出棧元素: nhooo
整數(shù)元素入棧: 
[1, 2]

Stack 基本上和 IntStack 相同,占位類型參數(shù) Element 代替了實(shí)際的 Int 類型。

以上示例中 Element 在如下三個(gè)地方被用作占位符:

  • 創(chuàng)建 items 屬性,使用 Element 類型的空數(shù)組對(duì)其進(jìn)行初始化。

  • 指定 push(_:) 方法的唯一參數(shù) item 的類型必須是 Element 類型。

  • 指定 pop() 方法的返回值類型必須是 Element 類型。

擴(kuò)展泛型類型

當(dāng)你擴(kuò)展一個(gè)泛型類型的時(shí)候(使用 extension 關(guān)鍵字),你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表。更加方便的是,原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展里是可以使用的,并且這些來自原始類型中的參數(shù)名稱會(huì)被用作原始定義中類型參數(shù)的引用。

下面的實(shí)例擴(kuò)展了泛型類型 Stack,為其添加了一個(gè)名為 topItem 的只讀計(jì)算型屬性,它將會(huì)返回當(dāng)前棧頂端的元素而不會(huì)將其從棧中移除:

泛型

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("nhooo")
 
if let topItem = stackOfStrings.topItem {
    print("棧中的頂部元素是:\(topItem).")
}
 
print(stackOfStrings.items)

示例中 topItem 屬性會(huì)返回一個(gè) Element 類型的可選值。當(dāng)棧為空的時(shí)候,topItem 會(huì)返回 nil;當(dāng)棧不為空的時(shí)候,topItem 會(huì)返回 items 數(shù)組中的最后一個(gè)元素。

以上程序執(zhí)行輸出結(jié)果為:

字符串元素入棧: 
棧中的頂部元素是:nhooo.
["google", "nhooo"]

我們也可以通過擴(kuò)展一個(gè)存在的類型來指定關(guān)聯(lián)類型。

例如 Swift 的 Array 類型已經(jīng)提供 append(_:) 方法,一個(gè) count 屬性,以及一個(gè)接受 Int 類型索引值的下標(biāo)用以檢索其元素。這三個(gè)功能都符合 Container 協(xié)議的要求,所以你只需簡(jiǎn)單地聲明 Array 采納該協(xié)議就可以擴(kuò)展 Array。

以下示例創(chuàng)建一個(gè)空擴(kuò)展即可:

extension Array: Container {}

類型約束

類型約束指定了一個(gè)必須繼承自指定類的類型參數(shù),或者遵循一個(gè)特定的協(xié)議或協(xié)議構(gòu)成。

類型約束語法

你可以寫一個(gè)在一個(gè)類型參數(shù)名后面的類型約束,通過冒號(hào)分割,來作為類型參數(shù)鏈的一部分。這種作用于泛型函數(shù)的類型約束的基礎(chǔ)語法如下所示(和泛型類型的語法相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這里是泛型函數(shù)的函數(shù)體部分
}

上面這個(gè)函數(shù)有兩個(gè)類型參數(shù)。第一個(gè)類型參數(shù) T,有一個(gè)要求 T 必須是 SomeClass 子類的類型約束;第二個(gè)類型參數(shù) U,有一個(gè)要求 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", "nhooo", "facebook"]
if let foundIndex = findIndex(ofString: "nhooo", in: strings) {
    print("nhooo 的索引為 \(foundIndex)")
}

索引下標(biāo)從 0 開始。

以上程序執(zhí)行輸出結(jié)果為:

nhooo 的索引為 3

關(guān)聯(lián)類

Swift 中使用 associatedtype 關(guān)鍵字來設(shè)置關(guān)聯(lián)類型示例。

下面實(shí)例定義了一個(gè) Container 協(xié)議,該協(xié)議定義了一個(gè)關(guān)聯(lián)類型 ItemType。

Container 協(xié)議只指定了三個(gè)任何遵從 Container 協(xié)議的類型必須提供的功能。遵從協(xié)議的類型在滿足這三個(gè)條件的情況下也可以提供其他額外的功能。

// Container 協(xié)議
protocol Container {
    associatedtype ItemType
    // 添加一個(gè)新元素到容器里
    mutating func append(_ item: ItemType)
    // 獲取容器中元素的數(shù)
    var count: Int { get }
    // 通過索引值類型為 Int 的下標(biāo)檢索到容器中的每一個(gè)元素
    subscript(i: Int) -> ItemType { get }
}
// Stack 結(jié)構(gòu)體遵從 Container 協(xié)議
struct Stack<Element>: Container {
    // Stack<Element> 的原始實(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<String>()
tos.push("google")
tos.push("nhooo")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素個(gè)數(shù)
print( tos.count)

以上程序執(zhí)行輸出結(jié)果為:

["google", "nhooo", "taobao"]
3

Where 語句

類型約束能夠確保類型符合泛型函數(shù)或類的定義約束。

你可以在參數(shù)列表中通過where語句定義參數(shù)的約束。

你可以寫一個(gè)where語句,緊跟在在類型參數(shù)列表后面,where語句后跟一個(gè)或者多個(gè)針對(duì)關(guān)聯(lián)類型的約束,以及(或)一個(gè)或多個(gè)類型和關(guān)聯(lián)類型間的等價(jià)(equality)關(guān)系。

在線示例

下面的實(shí)例定義了一個(gè)名為allItemsMatch的泛型函數(shù),用來檢查兩個(gè)Container示例是否包含相同順序的相同元素。

如果所有的元素能夠匹配,那么返回 true,反之則返回 false。

泛型

// Container 協(xié)議
protocol Container {
    associatedtype ItemType
    // 添加一個(gè)新元素到容器里
    mutating func append(_ item: ItemType)
    // 獲取容器中元素的數(shù)
    var count: Int { get }
    // 通過索引值類型為 Int 的下標(biāo)檢索到容器中的每一個(gè)元素
    subscript(i: Int) -> ItemType { get }
}
 
// // 遵循Container協(xié)議的泛型TOS類型
struct Stack<Element>: Container {
    // Stack<Element> 的原始實(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<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
        
        // 檢查兩個(gè)容器含有相同數(shù)量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 檢查每一對(duì)元素是否相等
        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("nhooo")
tos.push("taobao")
 
var aos = ["google", "nhooo", "taobao"]
 
if allItemsMatch(tos, aos) {
    print("匹配所有元素")
} else {
    print("元素不匹配")
}

以上程序執(zhí)行輸出結(jié)果為:

匹配所有元素
丰满人妻一级特黄a大片,午夜无码免费福利一级,欧美亚洲精品在线,国产婷婷成人久久Av免费高清