泛型

泛型代码可根据自定义需求,写出适用于任何类型、灵活且可重用的函数和类型,避免重复的代码,用一种清晰和抽象的思维表达代码的意思

1.泛型用途

    1. // 普通的函数,用来交换两个值  
    2. func swapTwoInts(inout a: Int, inout b: Int) {  
    3.     let temporaryA = a  
    4.     a = b  
    5.     b = temporaryA  
    6. }  
    7.   
    8. var someInt = 3  
    9. var anotherInt = 107  
    10. swapTwoInts(&someInt, &anotherInt)  
    11. println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")  
    12. // prints "someInt is now 107, and anotherInt is now 3"  
    13.   
    14. func swapTwoStrings(inout a: String, inout b: String) {  
    15.     let temporaryA = a  
    16.     a = b  
    17.     b = temporaryA  
    18. }  
    19.    
    20. func swapTwoDoubles(inout a: Double, inout b: Double) {  
    21.     let temporaryA = a  
    22.     a = b  
    23.     b = temporaryA  
    24. }

    以上都是转换两个数的值,但他们代码都是一样的,只不过传入类型不一样,同一份代码就要写了三次,使用泛型就可以忽略传入值的类型,只使用一份代码

    2.泛型函数

    占位符T是一种类型参数的示例,类型参数指定命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来,一旦类型参数被确定,就可以用来定义函数的参数类型(a,b),或作为函数的返回类型,或作为函数主题的注释类型,此时类型参数所代表的占位符不管函数任何时候被调用,都会被实际类型所替代

    1. func swapTwoValues<T>(inout a: T, inout b: T) {  
    2.     let temporaryA = a  
    3.     a = b  
    4.     b = temporaryA  
    5. }

    泛型版本的函数使用占位符(T)来替代实际类型名,占位符并没有提示T必须是什么类型,但a和b必须是用一种类型,在swapTwoValues被调用时才能确定T所表示的类型

      1. func swapTwoInts(inout a: Int, inout b: Int)  
      2. func swapTwoValues<T>(inout a: T, inout b: T)  
      3.   
      4. var someInt = 3  
      5. var anotherInt = 107  
      6. swapTwoValues(&someInt, &anotherInt)  
      7. // someInt is now 107, and anotherInt is now 3  
      8.    
      9. var someString = "hello"  
      10. var anotherString = "world"  
      11. swapTwoValues(&someString, &anotherString)  
      12. // someString is now "world", and anotherString is now "hello"

      3.泛型类型

      Swift允许自定义泛型类型,这些自定义类、结构体和枚举作用于任何类型

        1. // 以下是一个用泛型写的栈Stack,模拟栈的push和pop  
        2. struct IntStack {  
        3.     var items = Int[]()  
        4.     mutating func push(item: Int) {  
        5. .append(item)  
        6.     }  
        7.     mutating func pop() -> Int {  
        8. return items.removeLast()  
        9.     }  
        10. }  
        11.   
        12. // Stack提供两个方法,push和pop,从栈中压进一个值和弹出一个值,但只能用于int值,下面定义一个泛型Stack类,可处理任何类型的栈  
        13. struct Stack<T> {  
        14.     var items = T[]()  
        15.     mutating func push(item: T) {  
        16. .append(item)  
        17.     }  
        18.     mutating func pop() -> T {  
        19. return items.removeLast()  
        20.     }  
        21. }  
        22.   
        23. var stackOfStrings = Stack<String>()  
        24. stackOfStrings.push("uno")  
        25. stackOfStrings.push("dos")  
        26. stackOfStrings.push("tres")  
        27. stackOfStrings.push("cuatro")  
        28. // the stack now contains 4 strings  
        29.   
        30. let fromTheTop = stackOfStrings.pop()  
        31. // fromTheTop is equal to "cuatro", and the stack now contains 3 strings

        4.类型约束

        有时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定的类型是非常有用的,可以指定一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成

        4.1语法

          1. // 假定函数有两个类型参数,类型参数T必须是SomeClass子类的类型约束,类型参数U必须遵循SomeProtocol协议的类型约束  
          2. func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {  
          3. // function body goes here  
          4. }

          4.2实例

          非泛型函数,查找以给定的String数组,若找到匹配的字符串,返回下标

            1. func findStringIndex(array: String[], valueToFind: String) -> Int? {  
            2. for (index, value) in enumerate(array) {  
            3. if value == valueToFind {  
            4. return index  
            5.         }  
            6.     }  
            7. return nil  
            8. }  
            9.   
            10.   
            11. let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]  
            12. if let foundIndex = findStringIndex(strings, "llama") {  
            13. "The index of llama is \(foundIndex)")  
            14. }  
            15. // prints "The index of llama is 2"  
            16.   
            17.   
            18. // 使用泛型实现相同功能,但是下面这个泛型函数不会被编译,在等式 value == valueToFind 里,不是所有的类型都可以用等式 == 来进行比较,如果是自己创建的自定义类型,Swfit就无法猜到这个等于的意思,所以编译下面代码的时候就会报错  
            19. func findIndex<T>(array: T[], valueToFind: T) -> Int? {  
            20. for (index, value) in enumerate(array) {  
            21. if value == valueToFind {  
            22. return index  
            23.         }  
            24.     }  
            25. return nil  
            26. }

             Swift标准库定义了一个Equatable协议,要求任何遵循的类型实现等式 == 和不等式 != 对两个该类型进行比较。所有的Swift标准类型自动支持Equatable协议

              1. func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {  
              2. for (index, value) in enumerate(array) {  
              3. if value == valueToFind {  
              4. return index  
              5.         }  
              6.     }  
              7. return nil  
              8. }  
              9.   
              10. let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)  
              11. // doubleIndex is an optional Int with no value, because 9.3 is not in the array  
              12. let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")  
              13. // stringIndex is an optional Int containing a value of 2

              5.关联类型

              定义一个协议的时候,声明一个或多个关联类型作为协议定义的一部分是非常有用的,一个关联类型给定作用于协议部分的类型一个节点名。作用于关联类型上实际是不需要指定的,直到该协议接受。关联类型为 typealias关键字

              5.1实例

              定义ItemType关联类型,1. append方法添加一个新的item 2. count方法获取数量 3. 通过索引值检索到每一个item

                1. protocol Container {  
                2.     typealias ItemType  
                3.     mutating func append(item: ItemType)  
                4.  count: Int { get }  
                5.     subscript(i: Int) -> ItemType { get }  
                6. }  
                7.   
                8. // IntStack的非泛型版本,实现Container协议的所有三个要求  
                9. struct IntStack: Container {  
                10. // original IntStack implementation  
                11.     var items = Int[]()  
                12.     mutating func push(item: Int) {  
                13. .append(item)  
                14.     }  
                15.     mutating func pop() -> Int {  
                16. return items.removeLast()  
                17.     }  
                18. // conformance to the Container protocol  
                19.     typealias ItemType = Int  
                20.     mutating func append(item: Int) {  
                21. self.push(item)  
                22.     }  
                23.  count: Int {  
                24. return items.count  
                25.     }  
                26.     subscript(i: Int) -> Int {  
                27. return items[i]  
                28.     }  
                29. }  
                30.   
                31. // 遵循Container协议的泛型版本  
                32. struct Stack<T>: Container {  
                33. // original Stack<T> implementation  
                34.     var items = T[]()  
                35.     mutating func push(item: T) {  
                36. .append(item)  
                37.     }  
                38.     mutating func pop() -> T {  
                39. return items.removeLast()  
                40.     }  
                41. // conformance to the Container protocol  
                42.     mutating func append(item: T) {  
                43. self.push(item)  
                44.     }  
                45.  count: Int {  
                46. return items.count  
                47.     }  
                48.     subscript(i: Int) -> T {  
                49. return items[i]  
                50.     }  
                51. }

                5.2扩展一个存在的类型为已指定关联类型

                Swift中的Array已经提供了一个append方法,一个count属性和通过下标查找元素的功能,都已满足Container协议的要求,就意味着可以扩展Array去遵循Container协议,只要通过简单声明Array适用于该协议就可以了

                1. extension Array: Container {}


                6.Where语句

                where语句要求一个关联类型遵循一个特定的协议,或那个特定的类型参数和关联类型可以是相同的

                1. // 定义allItemsMatch的泛型函数检查两个Container单例是否包含相同顺序的相同元素,如果匹配返回ture,否则返回false  
                2. func allItemsMatch<  
                3.  C1: Container, C2: Container  
                4. 1.ItemType == C2.ItemType, C1.ItemType: Equatable>  
                5. 1, anotherContainer: C2) -> Bool {  
                6.           
                7. // check that both containers contain the same number of items  
                8. if someContainer.count != anotherContainer.count {  
                9. return false  
                10.         }  
                11.           
                12. // check each pair of items to see if they are equivalent  
                13. for i in 0..someContainer.count {  
                14. if someContainer[i] != anotherContainer[i] {  
                15. return false  
                16.             }  
                17.         }  
                18.           
                19. // all items match, so return true  
                20. return true  
                21.           
                22. }  
                23.   
                24. var stackOfStrings = Stack<String>()  
                25. stackOfStrings.push("uno")  
                26. stackOfStrings.push("dos")  
                27. stackOfStrings.push("tres")  
                28.    
                29. var arrayOfStrings = ["uno", "dos", "tres"]  
                30.    
                31. if allItemsMatch(stackOfStrings, arrayOfStrings) {  
                32. "All items match.")  
                33. } else {  
                34. "Not all items match.")  
                35. }  
                36. // prints "All items match."  
                37.