在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 泛型
方法 - Methods
關(guān)于 Swift
下標(biāo)腳本(Subscripts)
類和結(jié)構(gòu)體
類型轉(zhuǎn)換(Type Casting)
控制流
析構(gòu)過程(Deinitialization)
集合類型 (Collection Types)
構(gòu)造過程(Initialization)
Optional Chaining
枚舉(Enumerations)
自動(dòng)引用計(jì)數(shù)
繼承(Inheritance)
擴(kuò)展(Extensions)
泛型
字符串和字符(Strings and Characters)
函數(shù)(Functions)
高級(jí)運(yùn)算符
訪問控制
基本運(yùn)算符
嵌套類型
閉包(Closures)
協(xié)議
屬性 (Properties)

泛型

泛型代碼可以讓你寫出根據(jù)自我需求定義、適用于任何類型的,靈活且可重用的函數(shù)和類型。它的可以讓你避免重復(fù)的代碼,用一種清晰和抽象的方式來表達(dá)代碼的意圖。

泛型是 Swift 強(qiáng)大特征中的其中一個(gè),許多 Swift 標(biāo)準(zhǔn)庫是通過泛型代碼構(gòu)建出來的。事實(shí)上,泛型的使用貫穿了整本語言手冊(cè),只是你沒有發(fā)現(xiàn)而已。例如,Swift 的數(shù)組和字典類型都是泛型集。你可以創(chuàng)建一個(gè)Int數(shù)組,也可創(chuàng)建一個(gè)String數(shù)組,或者甚至于可以是任何其他 Swift 的類型數(shù)據(jù)數(shù)組。同樣的,你也可以創(chuàng)建存儲(chǔ)任何指定類型的字典(dictionary),而且這些類型可以是沒有限制的。

泛型所解決的問題

這里是一個(gè)標(biāo)準(zhǔn)的,非泛型函數(shù)swapTwoInts,用來交換兩個(gè)Int值:

    func swapTwoInts(inout a: Int, inout b: Int) {
        let temporaryA = a
        a = b
        b = temporaryA
    }

這個(gè)函數(shù)使用寫入讀出(in-out)參數(shù)來交換ab的值,請(qǐng)參考寫入讀出參數(shù)。

swapTwoInts函數(shù)可以交換b的原始值到a,也可以交換a的原始值到b,你可以調(diào)用這個(gè)函數(shù)交換兩個(gè)Int變量值:

    var someInt = 3
    var anotherInt = 107
    swapTwoInts(&someInt, &anotherInt)
    println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
    // 輸出 "someInt is now 107, and anotherInt is now 3"

swapTwoInts函數(shù)是非常有用的,但是它只能交換Int值,如果你想要交換兩個(gè)String或者Double,就不得不寫更多的函數(shù),如 swapTwoStringsswapTwoDoublesfunctions,如同如下所示:

    func swapTwoStrings(inout a: String, inout b: String) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    func swapTwoDoubles(inout a: Double, inout b: Double) {
        let temporaryA = a
        a = b
        b = temporaryA
    }

你可能注意到 swapTwoInts、 swapTwoStringsswapTwoDoubles函數(shù)功能都是相同的,唯一不同之處就在于傳入的變量類型不同,分別是IntStringDouble。

但實(shí)際應(yīng)用中通常需要一個(gè)用處更強(qiáng)大并且盡可能的考慮到更多的靈活性單個(gè)函數(shù),可以用來交換兩個(gè)任何類型值,很幸運(yùn)的是,泛型代碼幫你解決了這種問題。(一個(gè)這種泛型函數(shù)后面已經(jīng)定義好了。)

注意:
在所有三個(gè)函數(shù)中,ab的類型是一樣的。如果ab不是相同的類型,那它們倆就不能互換值。Swift 是類型安全的語言,所以它不允許一個(gè)String類型的變量和一個(gè)Double類型的變量互相交換值。如果一定要做,Swift 將報(bào)編譯錯(cuò)誤。

泛型函數(shù)

泛型函數(shù)可以工作于任何類型,這里是一個(gè)上面swapTwoInts函數(shù)的泛型版本,用于交換兩個(gè)值:

    func swapTwoValues<T>(inout a: T, inout b: T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }

swapTwoValues函數(shù)主體和swapTwoInts函數(shù)是一樣的,它只在第一行稍微有那么一點(diǎn)點(diǎn)不同于swapTwoInts,如下所示:

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

這個(gè)函數(shù)的泛型版本使用了占位類型名字(通常此情況下用字母T來表示)來代替實(shí)際類型名(如Int、StringDouble)。占位類型名沒有提示T必須是什么類型,但是它提示了ab必須是同一類型T,而不管T表示什么類型。只有swapTwoValues函數(shù)在每次調(diào)用時(shí)所傳入的實(shí)際類型才能決定T所代表的類型。

另外一個(gè)不同之處在于這個(gè)泛型函數(shù)名后面跟著的占位類型名字(T)是用尖括號(hào)括起來的(<T>)。這個(gè)尖括號(hào)告訴 Swift 那個(gè)TswapTwoValues函數(shù)所定義的一個(gè)類型。因?yàn)?code>T是一個(gè)占位命名類型,Swift 不會(huì)去查找命名為T的實(shí)際類型。

swapTwoValues函數(shù)除了要求傳入的兩個(gè)任何類型值是同一類型外,也可以作為swapTwoInts函數(shù)被調(diào)用。每次swapTwoValues被調(diào)用,T所代表的類型值都會(huì)傳給函數(shù)。

在下面的兩個(gè)例子中,T分別代表IntString

    var someInt = 3
    var anotherInt = 107
    swapTwoValues(&someInt, &anotherInt)
    // someInt is now 107, and anotherInt is now 3
    var someString = "hello"
    var anotherString = "world"
    swapTwoValues(&someString, &anotherString)
    // someString is now "world", and anotherString is now "hello"

注意
上面定義的函數(shù)swapTwoValues是受swap函數(shù)啟發(fā)而實(shí)現(xiàn)的。swap函數(shù)存在于 Swift 標(biāo)準(zhǔn)庫,并可以在其它類中任意使用。如果你在自己代碼中需要類似swapTwoValues函數(shù)的功能,你可以使用已存在的交換函數(shù)swap函數(shù)。

類型參數(shù)

在上面的swapTwoValues例子中,占位類型T是一種類型參數(shù)的示例。類型參數(shù)指定并命名為一個(gè)占位類型,并且緊隨在函數(shù)名后面,使用一對(duì)尖括號(hào)括起來(如<T>)。

一旦一個(gè)類型參數(shù)被指定,那么其可以被使用來定義一個(gè)函數(shù)的參數(shù)類型(如swapTwoValues函數(shù)中的參數(shù)ab),或作為一個(gè)函數(shù)返回類型,或用作函數(shù)主體中的注釋類型。在這種情況下,被類型參數(shù)所代表的占位類型不管函數(shù)任何時(shí)候被調(diào)用,都會(huì)被實(shí)際類型所替換(在上面swapTwoValues例子中,當(dāng)函數(shù)第一次被調(diào)用時(shí),TInt替換,第二次調(diào)用時(shí),被String替換。)。

你可支持多個(gè)類型參數(shù),命名在尖括號(hào)中,用逗號(hào)分開。

命名類型參數(shù)

在簡(jiǎn)單的情況下,泛型函數(shù)或泛型類型需要指定一個(gè)占位類型(如上面的swapTwoValues泛型函數(shù),或一個(gè)存儲(chǔ)單一類型的泛型集,如數(shù)組),通常用一單個(gè)字母T來命名類型參數(shù)。不過,你可以使用任何有效的標(biāo)識(shí)符來作為類型參數(shù)名。

如果你使用多個(gè)參數(shù)定義更復(fù)雜的泛型函數(shù)或泛型類型,那么使用更多的描述類型參數(shù)是非常有用的。例如,Swift 字典(Dictionary)類型有兩個(gè)類型參數(shù),一個(gè)是鍵,另外一個(gè)是值。如果你自己寫字典,你或許會(huì)定義這兩個(gè)類型參數(shù)為KeyTypeValueType,用來記住它們?cè)谀愕姆盒痛a中的作用。

注意
請(qǐng)始終使用大寫字母開頭的駝峰式命名法(例如TKeyType)來給類型參數(shù)命名,以表明它們是類型的占位符,而非類型值。

泛型類型

通常在泛型函數(shù)中,Swift 允許你定義你自己的泛型類型。這些自定義類、結(jié)構(gòu)體和枚舉作用于任何類型,如同ArrayDictionary的用法。

這部分向你展示如何寫一個(gè)泛型集類型--Stack(棧)。一個(gè)棧是一系列值域的集合,和Array(數(shù)組)類似,但其是一個(gè)比 Swift 的Array類型更多限制的集合。一個(gè)數(shù)組可以允許其里面任何位置的插入/刪除操作,而棧,只允許在集合的末端添加新的項(xiàng)(如同push一個(gè)新值進(jìn)棧)。同樣的一個(gè)棧也只能從末端移除項(xiàng)(如同pop一個(gè)值出棧)。

注意
棧的概念已被UINavigationController類使用來模擬試圖控制器的導(dǎo)航結(jié)構(gòu)。你通過調(diào)用UINavigationControllerpushViewController:animated:方法來為導(dǎo)航棧添加(add)新的試圖控制器;而通過popViewControllerAnimated:的方法來從導(dǎo)航棧中移除(pop)某個(gè)試圖控制器。每當(dāng)你需要一個(gè)嚴(yán)格的后進(jìn)先出方式來管理集合,堆棧都是最實(shí)用的模型。

下圖展示了一個(gè)棧的壓棧(push)/出棧(pop)的行為:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Generics_1.png" alt="Image of Generics_1.png" />

  1. 現(xiàn)在有三個(gè)值在棧中;
  2. 第四個(gè)值“pushed”到棧的頂部;
  3. 現(xiàn)在有四個(gè)值在棧中,最近的那個(gè)在頂部;
  4. 棧中最頂部的那個(gè)項(xiàng)被移除,或稱之為“popped”;
  5. 移除掉一個(gè)值后,現(xiàn)在棧又重新只有三個(gè)值。

這里展示了如何寫一個(gè)非泛型版本的棧,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è)Array性質(zhì)的items存儲(chǔ)值。Stack提供兩個(gè)方法:pushpop,從棧中壓進(jìn)一個(gè)值和移除一個(gè)值。這些方法標(biāo)記為可變的,因?yàn)樗鼈冃枰薷模ɑ?em>轉(zhuǎn)換)結(jié)構(gòu)體的items數(shù)組。

上面所展現(xiàn)的IntStack類型只能用于Int值,不過,其對(duì)于定義一個(gè)泛型Stack類(可以處理任何類型值的棧)是非常有用的。

這里是一個(gè)相同代碼的泛型版本:

    struct Stack<T> {
        var items = [T]()
        mutating func push(item: T) {
            items.append(item)
        }
        mutating func pop() -> T {
            return items.removeLast()
        }
    }

注意到Stack的泛型版本基本上和非泛型版本相同,但是泛型版本的占位類型參數(shù)為T代替了實(shí)際Int類型。這種類型參數(shù)包含在一對(duì)尖括號(hào)里(<T>),緊隨在結(jié)構(gòu)體名字后面。

T定義了一個(gè)名為“某種類型T”的節(jié)點(diǎn)提供給后來用。這種將來類型可以在結(jié)構(gòu)體的定義里任何地方表示為“T”。在這種情況下,T在如下三個(gè)地方被用作節(jié)點(diǎn):

  • 創(chuàng)建一個(gè)名為items的屬性,使用空的T類型值數(shù)組對(duì)其進(jìn)行初始化;
  • 指定一個(gè)包含一個(gè)參數(shù)名為itempush方法,該參數(shù)必須是T類型;
  • 指定一個(gè)pop方法的返回值,該返回值將是一個(gè)T類型值。

當(dāng)創(chuàng)建一個(gè)新單例并初始化時(shí), 通過用一對(duì)緊隨在類型名后的尖括號(hào)里寫出實(shí)際指定棧用到類型,創(chuàng)建一個(gè)Stack實(shí)例,同創(chuàng)建ArrayDictionary一樣:

    var stackOfStrings = Stack<String>()
    stackOfStrings.push("uno")
    stackOfStrings.push("dos")
    stackOfStrings.push("tres")
    stackOfStrings.push("cuatro")
    // 現(xiàn)在棧已經(jīng)有4個(gè)string了

下圖將展示stackOfStrings如何push這四個(gè)值進(jìn)棧的過程:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Generics_2.png" alt="Image of Generics_2.png" />

從棧中pop并移除值"cuatro":

    let fromTheTop = stackOfStrings.pop()
    // fromTheTop is equal to "cuatro", and the stack now contains 3 strings

下圖展示了如何從棧中pop一個(gè)值的過程:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Generics_3.png" alt="Image of Generics_3.png" />

由于Stack是泛型類型,所以在 Swift 中其可以用來創(chuàng)建任何有效類型的棧,這種方式如同ArrayDictionary。

類型約束

swapTwoValues函數(shù)和Stack類型可以作用于任何類型,不過,有的時(shí)候?qū)κ褂迷诜盒秃瘮?shù)和泛型類型上的類型強(qiáng)制約束為某種特定類型是非常有用的。類型約束指定了一個(gè)必須繼承自指定類的類型參數(shù),或者遵循一個(gè)特定的協(xié)議或協(xié)議構(gòu)成。

例如,Swift 的Dictionary類型對(duì)作用于其鍵的類型做了些限制。在字典的描述中,字典的鍵類型必須是可哈希,也就是說,必須有一種方法可以使其被唯一的表示。Dictionary之所以需要其鍵是可哈希是為了以便于其檢查其是否已經(jīng)包含某個(gè)特定鍵的值。如無此需求,Dictionary既不會(huì)告訴是否插入或者替換了某個(gè)特定鍵的值,也不能查找到已經(jīng)存儲(chǔ)在字典里面的給定鍵值。

這個(gè)需求強(qiáng)制加上一個(gè)類型約束作用于Dictionary的鍵上,當(dāng)然其鍵類型必須遵循Hashable協(xié)議(Swift 標(biāo)準(zhǔn)庫中定義的一個(gè)特定協(xié)議)。所有的 Swift 基本類型(如String,IntDoubleBool)默認(rèn)都是可哈希。

當(dāng)你創(chuàng)建自定義泛型類型時(shí),你可以定義你自己的類型約束,當(dāng)然,這些約束要支持泛型編程的強(qiáng)力特征中的多數(shù)。抽象概念如可哈希具有的類型特征是根據(jù)它們概念特征來界定的,而不是它們的直接類型特征。

類型約束語法

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

    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
        // function body goes here
    }

上面這個(gè)假定函數(shù)有兩個(gè)類型參數(shù)。第一個(gè)類型參數(shù)T,有一個(gè)需要T必須是SomeClass子類的類型約束;第二個(gè)類型參數(shù)U,有一個(gè)需要U必須遵循SomeProtocol協(xié)議的類型約束。

類型約束行為

這里有個(gè)名為findStringIndex的非泛型函數(shù),該函數(shù)功能是去查找包含一給定String值的數(shù)組。若查找到匹配的字符串,findStringIndex函數(shù)返回該字符串在數(shù)組中的索引值(Int),反之則返回nil

    func findStringIndex(array: [String], valueToFind: String) -> Int? {
        for (index, value) in enumerate(array) {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }

findStringIndex函數(shù)可以作用于查找一字符串?dāng)?shù)組中的某個(gè)字符串:

    let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
    if let foundIndex = findStringIndex(strings, "llama") {
        println("The index of llama is \(foundIndex)")
    }
    // 輸出 "The index of llama is 2"

如果只是針對(duì)字符串而言查找在數(shù)組中的某個(gè)值的索引,用處不是很大,不過,你可以寫出相同功能的泛型函數(shù)findIndex,用某個(gè)類型T值替換掉提到的字符串。

這里展示如何寫一個(gè)你或許期望的findStringIndex的泛型版本findIndex。請(qǐng)注意這個(gè)函數(shù)仍然返回Int,是不是有點(diǎn)迷惑呢,而不是泛型類型?那是因?yàn)楹瘮?shù)返回的是一個(gè)可選的索引數(shù),而不是從數(shù)組中得到的一個(gè)可選值。需要提醒的是,這個(gè)函數(shù)不會(huì)編譯,原因在例子后面會(huì)說明:

    func findIndex<T>(array: T[], valueToFind: T) -> Int? {
        for (index, value) in enumerate(array) {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }

上面所寫的函數(shù)不會(huì)編譯。這個(gè)問題的位置在等式的檢查上,“if value == valueToFind”。不是所有的 Swift 中的類型都可以用等式符(==)進(jìn)行比較。例如,如果你創(chuàng)建一個(gè)你自己的類或結(jié)構(gòu)體來表示一個(gè)復(fù)雜的數(shù)據(jù)模型,那么 Swift 沒法猜到對(duì)于這個(gè)類或結(jié)構(gòu)體而言“等于”的意思。正因如此,這部分代碼不能可能保證工作于每個(gè)可能的類型T,當(dāng)你試圖編譯這部分代碼時(shí)估計(jì)會(huì)出現(xiàn)相應(yīng)的錯(cuò)誤。

不過,所有的這些并不會(huì)讓我們無從下手。Swift 標(biāo)準(zhǔn)庫中定義了一個(gè)Equatable協(xié)議,該協(xié)議要求任何遵循的類型實(shí)現(xiàn)等式符(==)和不等符(!=)對(duì)任何兩個(gè)該類型進(jìn)行比較。所有的 Swift 標(biāo)準(zhǔn)類型自動(dòng)支持Equatable協(xié)議。

任何Equatable類型都可以安全的使用在findIndex函數(shù)中,因?yàn)槠浔WC支持等式操作。為了說明這個(gè)事實(shí),當(dāng)你定義一個(gè)函數(shù)時(shí),你可以寫一個(gè)Equatable類型約束作為類型參數(shù)定義的一部分:

    func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
        for (index, value) in enumerate(array) {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }

findIndex中這個(gè)單個(gè)類型參數(shù)寫做:T: Equatable,也就意味著“任何T類型都遵循Equatable協(xié)議”。

findIndex函數(shù)現(xiàn)在則可以成功的編譯過,并且作用于任何遵循Equatable的類型,如DoubleString:

    let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
    // doubleIndex is an optional Int with no value, because 9.3 is not in the array
    let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
    // stringIndex is an optional Int containing a value of 2

關(guān)聯(lián)類型(Associated Types)

當(dāng)定義一個(gè)協(xié)議時(shí),有的時(shí)候聲明一個(gè)或多個(gè)關(guān)聯(lián)類型作為協(xié)議定義的一部分是非常有用的。一個(gè)關(guān)聯(lián)類型作為協(xié)議的一部分,給定了類型的一個(gè)占位名(或別名)。作用于關(guān)聯(lián)類型上實(shí)際類型在協(xié)議被實(shí)現(xiàn)前是不需要指定的。關(guān)聯(lián)類型被指定為typealias關(guān)鍵字。

關(guān)聯(lián)類型行為

這里是一個(gè)Container協(xié)議的例子,定義了一個(gè)ItemType關(guān)聯(lián)類型:

    protocol Container {
        typealias ItemType
        mutating func append(item: ItemType)
        var count: Int { get }
        subscript(i: Int) -> ItemType { get }
    }

Container協(xié)議定義了三個(gè)任何容器必須支持的兼容要求:

  • 必須可能通過append方法添加一個(gè)新item到容器里;
  • 必須可能通過使用count屬性獲取容器里items的數(shù)量,并返回一個(gè)Int值;
  • 必須可能通過容器的Int索引值下標(biāo)可以檢索到每一個(gè)item。

這個(gè)協(xié)議沒有指定容器里item是如何存儲(chǔ)的或何種類型是允許的。這個(gè)協(xié)議只指定三個(gè)任何遵循Container類型所必須支持的功能點(diǎn)。一個(gè)遵循的類型在滿足這三個(gè)條件的情況下也可以提供其他額外的功能。

任何遵循Container協(xié)議的類型必須指定存儲(chǔ)在其里面的值類型,必須保證只有正確類型的items可以加進(jìn)容器里,必須明確可以通過其下標(biāo)返回item類型。

為了定義這三個(gè)條件,Container協(xié)議需要一個(gè)方法指定容器里的元素將會(huì)保留,而不需要知道特定容器的類型。Container協(xié)議需要指定任何通過append方法添加到容器里的值和容器里元素是相同類型,并且通過容器下標(biāo)返回的容器元素類型的值的類型是相同類型。

為了達(dá)到此目的,Container協(xié)議聲明了一個(gè)ItemType的關(guān)聯(lián)類型,寫作typealias ItemType。這個(gè)協(xié)議不會(huì)定義ItemType是什么的別名,這個(gè)信息將由任何遵循協(xié)議的類型來提供。盡管如此,ItemType別名提供了一種識(shí)別Container中Items類型的方法,并且用于append方法和subscript方法的類型定義,以便保證任何Container期望的行為能夠被執(zhí)行。

這里是一個(gè)早前IntStack類型的非泛型版本,遵循Container協(xié)議:

    struct IntStack: Container {
        // IntStack的原始實(shí)現(xiàn)
        var items = [Int]()
        mutating func push(item: Int) {
            items.append(item)
        }
        mutating func pop() -> Int {
            return items.removeLast()
        }
        // 遵循Container協(xié)議的實(shí)現(xiàn)
        typealias ItemType = Int
        mutating func append(item: Int) {
            self.push(item)
        }
        var count: Int {
        return items.count
        }
        subscript(i: Int) -> Int {
            return items[i]
        }
    }

IntStack類型實(shí)現(xiàn)了Container協(xié)議的所有三個(gè)要求,在IntStack類型的每個(gè)包含部分的功能都滿足這些要求。

此外,IntStack指定了Container的實(shí)現(xiàn),適用的ItemType被用作Int類型。對(duì)于這個(gè)Container協(xié)議實(shí)現(xiàn)而言,定義 typealias ItemType = Int,將抽象的ItemType類型轉(zhuǎn)換為具體的Int類型。

感謝Swift類型參考,你不用在IntStack定義部分聲明一個(gè)具體的IntItemType。由于IntStack遵循Container協(xié)議的所有要求,只要通過簡(jiǎn)單的查找append方法的item參數(shù)類型和下標(biāo)返回的類型,Swift就可以推斷出合適的ItemType來使用。確實(shí),如果上面的代碼中你刪除了 typealias ItemType = Int這一行,一切仍舊可以工作,因?yàn)樗宄闹繧temType使用的是何種類型。

你也可以生成遵循Container協(xié)議的泛型Stack類型:

    struct Stack<T>: Container {
        // original Stack<T> implementation
        var items = [T]()
        mutating func push(item: T) {
            items.append(item)
        }
        mutating func pop() -> T {
            return items.removeLast()
        }
        // conformance to the Container protocol
        mutating func append(item: T) {
            self.push(item)
        }
        var count: Int {
        return items.count
        }
        subscript(i: Int) -> T {
            return items[i]
        }
    }

這個(gè)時(shí)候,占位類型參數(shù)T被用作append方法的item參數(shù)和下標(biāo)的返回類型。Swift 因此可以推斷出被用作這個(gè)特定容器的ItemTypeT的合適類型。

擴(kuò)展一個(gè)存在的類型為一指定關(guān)聯(lián)類型

使用擴(kuò)展來添加協(xié)議兼容性中有描述擴(kuò)展一個(gè)存在的類型添加遵循一個(gè)協(xié)議。這個(gè)類型包含一個(gè)關(guān)聯(lián)類型的協(xié)議。

Swift的Array已經(jīng)提供append方法,一個(gè)count屬性和通過下標(biāo)來查找一個(gè)自己的元素。這三個(gè)功能都達(dá)到Container協(xié)議的要求。也就意味著你可以擴(kuò)展Array去遵循Container協(xié)議,只要通過簡(jiǎn)單聲明Array適用于該協(xié)議而已。如何實(shí)踐這樣一個(gè)空擴(kuò)展,在使用擴(kuò)展來聲明協(xié)議的采納中有描述這樣一個(gè)實(shí)現(xiàn)一個(gè)空擴(kuò)展的行為:

    extension Array: Container {}

如同上面的泛型Stack類型一樣,Array的append方法和下標(biāo)保證Swift可以推斷出ItemType所使用的適用的類型。定義了這個(gè)擴(kuò)展后,你可以將任何Array當(dāng)作Container來使用。

Where 語句

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

對(duì)關(guān)聯(lián)類型定義約束是非常有用的。你可以在參數(shù)列表中通過where語句定義參數(shù)的約束。一個(gè)where語句能夠使一個(gè)關(guān)聯(lián)類型遵循一個(gè)特定的協(xié)議,以及(或)那個(gè)特定的類型參數(shù)和關(guān)聯(lián)類型可以是相同的。你可以寫一個(gè)where語句,緊跟在在類型參數(shù)列表后面,where語句后跟一個(gè)或者多個(gè)針對(duì)關(guān)聯(lián)類型的約束,以及(或)一個(gè)或多個(gè)類型和關(guān)聯(lián)類型間的等價(jià)(equality)關(guān)系。

下面的例子定義了一個(gè)名為allItemsMatch的泛型函數(shù),用來檢查兩個(gè)Container實(shí)例是否包含相同順序的相同元素。如果所有的元素能夠匹配,那么返回一個(gè)為trueBoolean值,反之則為false。

被檢查的兩個(gè)Container可以不是相同類型的容器(雖然它們可以是),但它們確實(shí)擁有相同類型的元素。這個(gè)需求通過一個(gè)類型約束和where語句結(jié)合來表示:

    func allItemsMatch<
        C1: Container, C2: Container
        where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
        (someContainer: C1, anotherContainer: C2) -> Bool {

            // 檢查兩個(gè)Container的元素個(gè)數(shù)是否相同
            if someContainer.count != anotherContainer.count {
                return false
            }

        // 檢查兩個(gè)Container相應(yīng)位置的元素彼此是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }

        // 如果所有元素檢查都相同則返回true
        return true

    }

這個(gè)函數(shù)用了兩個(gè)參數(shù):someContaineranotherContainer。someContainer參數(shù)是類型C1,anotherContainer參數(shù)是類型C2C1C2是容器的兩個(gè)占位類型參數(shù),決定了這個(gè)函數(shù)何時(shí)被調(diào)用。

這個(gè)函數(shù)的類型參數(shù)列緊隨在兩個(gè)類型參數(shù)需求的后面:

  • C1必須遵循Container協(xié)議 (寫作 C1: Container)。
  • C2必須遵循Container協(xié)議 (寫作 C2: Container)。
  • C1ItemType同樣是C2的ItemType(寫作 C1.ItemType == C2.ItemType)。
  • C1ItemType必須遵循Equatable協(xié)議 (寫作 C1.ItemType: Equatable)。

第三個(gè)和第四個(gè)要求被定義為一個(gè)where語句的一部分,寫在關(guān)鍵字where后面,作為函數(shù)類型參數(shù)鏈的一部分。

這些要求意思是:

someContainer是一個(gè)C1類型的容器。 anotherContainer是一個(gè)C2類型的容器。 someContaineranotherContainer包含相同的元素類型。 someContainer中的元素可以通過不等于操作(!=)來檢查它們是否彼此不同。

第三個(gè)和第四個(gè)要求結(jié)合起來的意思是anotherContainer中的元素也可以通過 != 操作來檢查,因?yàn)樗鼈冊(cè)?code>someContainer中元素確實(shí)是相同的類型。

這些要求能夠使allItemsMatch函數(shù)比較兩個(gè)容器,即便它們是不同的容器類型。

allItemsMatch首先檢查兩個(gè)容器是否擁有同樣數(shù)目的items,如果它們的元素?cái)?shù)目不同,沒有辦法進(jìn)行匹配,函數(shù)就會(huì)false

檢查完之后,函數(shù)通過for-in循環(huán)和半閉區(qū)間操作(..)來迭代someContainer中的所有元素。對(duì)于每個(gè)元素,函數(shù)檢查是否someContainer中的元素不等于對(duì)應(yīng)的anotherContainer中的元素,如果這兩個(gè)元素不等,則這兩個(gè)容器不匹配,返回false。

如果循環(huán)體結(jié)束后未發(fā)現(xiàn)沒有任何的不匹配,那表明兩個(gè)容器匹配,函數(shù)返回true。

這里演示了allItemsMatch函數(shù)運(yùn)算的過程:

    var stackOfStrings = Stack<String>()
    stackOfStrings.push("uno")
    stackOfStrings.push("dos")
    stackOfStrings.push("tres")

    var arrayOfStrings = ["uno", "dos", "tres"]

    if allItemsMatch(stackOfStrings, arrayOfStrings) {
        println("All items match.")
    } else {
        println("Not all items match.")
    }
    // 輸出 "All items match."

上面的例子創(chuàng)建一個(gè)Stack單例來存儲(chǔ)String,然后壓了三個(gè)字符串進(jìn)棧。這個(gè)例子也創(chuàng)建了一個(gè)Array單例,并初始化包含三個(gè)同棧里一樣的原始字符串。即便棧和數(shù)組是不同的類型,但它們都遵循Container協(xié)議,而且它們都包含同樣的類型值。因此你可以調(diào)用allItemsMatch函數(shù),用這兩個(gè)容器作為它的參數(shù)。在上面的例子中,allItemsMatch函數(shù)正確的顯示了所有的這兩個(gè)容器的items匹配。

上一篇:嵌套類型下一篇:控制流