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

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

泛型

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

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

泛型所解決的問(wèn)題

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

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

這個(gè)函數(shù)使用寫(xiě)入讀出(in-out)參數(shù)來(lái)交換ab的值,請(qǐng)參考寫(xiě)入讀出參數(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,就不得不寫(xiě)更多的函數(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ù)功能都是相同的,唯一不同之處就在于傳入的變量類型不同,分別是Int、StringDouble。

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

注意:
在所有三個(gè)函數(shù)中,ab的類型是一樣的。如果ab不是相同的類型,那它們倆就不能互換值。Swift 是類型安全的語(yǔ)言,所以它不允許一個(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來(lái)表示)來(lái)代替實(shí)際類型名(如Int、StringDouble)。占位類型名沒(méi)有提示T必須是什么類型,但是它提示了ab必須是同一類型T,而不管T表示什么類型。只有swapTwoValues函數(shù)在每次調(diào)用時(shí)所傳入的實(shí)際類型才能決定T所代表的類型。

另外一個(gè)不同之處在于這個(gè)泛型函數(shù)名后面跟著的占位類型名字(T)是用尖括號(hào)括起來(lái)的(<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)庫(kù),并可以在其它類中任意使用。如果你在自己代碼中需要類似swapTwoValues函數(shù)的功能,你可以使用已存在的交換函數(shù)swap函數(shù)。

類型參數(shù)

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

一旦一個(gè)類型參數(shù)被指定,那么其可以被使用來(lái)定義一個(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)分開(kāi)。

命名類型參數(shù)

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

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

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

泛型類型

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

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

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

這里展示了如何寫(xiě)一個(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值,不過(guò),其對(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)提供給后來(lái)用。這種將來(lái)類型可以在結(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í), 通過(guò)用一對(duì)緊隨在類型名后的尖括號(hào)里寫(xiě)出實(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)棧的過(guò)程:

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è)值的過(guò)程:

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

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

類型約束

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

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

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

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

類型約束語(yǔ)法

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

    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è)值的索引,用處不是很大,不過(guò),你可以寫(xiě)出相同功能的泛型函數(shù)findIndex,用某個(gè)類型T值替換掉提到的字符串。

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

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

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

不過(guò),所有的這些并不會(huì)讓我們無(wú)從下手。Swift 標(biāo)準(zhǔn)庫(kù)中定義了一個(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支持等式操作。為了說(shuō)明這個(gè)事實(shí),當(dāng)你定義一個(gè)函數(shù)時(shí),你可以寫(xiě)一個(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ù)寫(xiě)做:T: Equatable,也就意味著“任何T類型都遵循Equatable協(xié)議”。

findIndex函數(shù)現(xiàn)在則可以成功的編譯過(guò),并且作用于任何遵循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è)任何容器必須支持的兼容要求:

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

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

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

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

為了達(dá)到此目的,Container協(xié)議聲明了一個(gè)ItemType的關(guān)聯(lián)類型,寫(xiě)作typealias ItemType。這個(gè)協(xié)議不會(huì)定義ItemType是什么的別名,這個(gè)信息將由任何遵循協(xié)議的類型來(lái)提供。盡管如此,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é)議的所有要求,只要通過(guò)簡(jiǎn)單的查找append方法的item參數(shù)類型和下標(biāo)返回的類型,Swift就可以推斷出合適的ItemType來(lái)使用。確實(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ò)展來(lái)添加協(xié)議兼容性中有描述擴(kuò)展一個(gè)存在的類型添加遵循一個(gè)協(xié)議。這個(gè)類型包含一個(gè)關(guān)聯(lián)類型的協(xié)議。

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

    extension Array: Container {}

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

Where 語(yǔ)句

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

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

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

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

    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ù)是類型C1anotherContainer參數(shù)是類型C2。C1C2是容器的兩個(gè)占位類型參數(shù),決定了這個(gè)函數(shù)何時(shí)被調(diào)用。

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

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

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

這些要求意思是:

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

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

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

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

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

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

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

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

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