Swift泛型

作者: 蓝色达风 | 来源:发表于2017-04-13 16:31 被阅读68次

    前言

    OC缺乏一个重要特性,不支持泛型。Swift拥有了这一特性,是灵活性的语法,在函数、结构体、类、枚举中都可以应用,相当于暂位符的作用,当类型暂时不确定,只有等到调用函数时才能确定具体类型的时候可以引入泛型。

    简单理解泛型就是先占位,具体占位做什么,用的时候再说。Swift的Array和Dictionary类型都是泛型集

    应用场景

    简单模仿一个栈操作,主要实现出栈、入栈两个功能

    // MARK: - 模仿栈
    class TestStack {
        var valueArray: [Int] = []
        
        /// 压栈
        func push(value: Int) -> () {
            valueArray.append(value)
        }
        
        /// 出栈
        func pop() -> (Int?) {
            if let lastValue = valueArray.last {
                valueArray.removeLast()
                return lastValue
                
            }else {
                return nil
            }
        }
    }
    

    这样实现的栈只能操作Int类型,如果需求变更,要处理String类型,怎么解决?替换所有Int为String可以解决,但这种代码明显上不了台面。此外另外我们还会想到Any类型,如下

    // MARK: - 模仿栈
    class TestStack {
        var valueArray: [Any] = []
        
        /// 压栈
        func push(value: Any) -> () {
            valueArray.append(value)
        }
        
        /// 出栈
        func pop() -> (Any?) {
            if let lastValue = valueArray.last {
                valueArray.removeLast()
                return lastValue
                
            }else {
                return nil
            }
        }
    }
    

    如此TestStack可操作类型就不局限于一种了,但是带来了两个问题:1、数据类型安全问题;2、每次对栈进行操作时,都需要进行一系列繁琐的类型转换(casting操作,使用as来进行类型转换)

    这里简单介绍下Any和AnyObject的区别:

    在 Swift 3 之前,我们可以写完一个项目都只用 AnyObject 来代表大多数实例,好像不用与 Any 类型打交道。但事实上,Any 和 AnyObject 是有明显区别的,因为 Any 可以代表 struct、class、func 等等几乎所有类型,而 AnyObject 只能代表 class 生成的实例。

    那为什么之前我们在 Swift 2 里可以用 [AnyObject] 声明数组,并且在里面放 Int、String 等 struct 类型呢?这是因为 Swift 2 中,会针对这些 Int、String 等 struct 进行一个 Implicit Bridging Conversions,在 Array 里插入他们时,编译器会自动将其 bridge 到 Objective-C 的 NSNumber、NSString 等类型,这就是为什么我们声明的 [AnyObject] 里可以放 struct 的原因。

    但在 Swift 3 当中,为了达成一个门真正的跨平台语言,相关提案将 Implicit Bridging Conversions 给去掉了。所以如果你要把 String 这个 struct 放进一个 [AnyObject] 里,一定要 as NSString,这些转换都需要显示的进行了——毕竟 Linux 平台默认没有 Objective-C runtime。这样各平台的表现更加一致。当然这是其中一个目标。

    参照泛型的特性,定义一个泛型类型。使用泛型后的示例代码如下:
    `
    // MARK: - 模仿栈
    class TestStack<T> {
    var valueArray: [T] = []

    /// 压栈
    func push(value: T) -> () {
        valueArray.append(value)
    }
    
    /// 出栈
    func pop() -> (T?) {
        if let lastValue = valueArray.last {
            valueArray.removeLast()
            return lastValue
            
        }else {
            return nil
        }
    }
    

    }
    `

    使用泛型:在初始化时通过明确的类型(这里用Int示例)来定义参数,之后编译器会将所有的泛型(T)替换成Int类型(如此实现的栈,最大优势在于能够匹配任何类型)。

           // 示例1:Int
            let myStack1 = TestStack<Int>()
            myStack1.push(value: 1)
            myStack1.push(value: 2)
            myStack1.push(value: 3)
            print(myStack1.pop() ?? "0")
            
            // 示例2:String
            let myStack2 = TestStack<String>()
            myStack2.push(value: "a")
            myStack2.push(value: "b")
            myStack2.push(value: "c")
            print(myStack2.pop() ?? "0")
    

    泛型约束

    泛型约束大致分为以下几种:

    协议约束,泛型类型必须遵循某些协议
    继承约束,泛型类型必须是某个类的子类类型
    条件约束,泛型类型必须满足某种条件

    协议约束

    还拿TestStack栈说事儿,这里添加一个函数isContainValue,要判断栈中是否包含传入的元素,需要用到等式符(==)和不等符(!=)对任何两个该类型进行比较。Swift标准库中定义了一个Equatable协议,只有支持了这个协议才可以使用等式符(==)和不等符(!=),否则报红。所有的Swift标准类型自动支持Equatable协议,但是泛型由于不确定类型,系统不知对象是否支持Equatable协议,所以直接使用==或!=报红


    泛型协议约束.png

    解决方案,让TestStack栈中,让泛型T遵循Equatable协议


    泛型协议约束1.png
    继承约束

    这里定义了三个类Person、Leader、Coder,其中Leader是继承Person,

    // MARK: - 人
    class Person: NSObject {
        func watchTV() -> () {
            print("Person看电视:人民的名义")
        }
    }
    
    
    // MARK: - 领导
    class Leader: Person {
        override func watchTV() -> () {
            print("Leader看电视:人民的名义")
        }
    }
    
    
    // MARK: - 程序员
    class Coder {
        func watchTV() -> () {
            print("coder看电视:人民的名义")
        }
    }
    
    泛型继承约束1.png
        func testWatchTV<T: Person>(obj:T) -> () {
            obj.watchTV()
        }
    

    testWatchTV函数,接受一个泛型参数,要求该泛型类型必须继承Person,否则爆红。

    条件约束

    在类型名后面使用where来指定对类型的特殊需求,比如限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类等

    // 定义一个协议
    @objc protocol TestProtocol {
        @objc optional func testOne() -> ()
    }
    
    // MARK: - 人
    class Person: NSObject, TestProtocol {
        var name: String?
        
        func watchTV() -> () {
            print("Person看电视:人民的名义")
        }
    }
    
    
    // MARK: - 领导
    class Leader: NSObject {
        var name: String?
        
        func watchTV() -> () {
            print("Leader看电视:人民的名义")
        }
    }
    
    泛型条件约束1.png

    testCondition函数要求传入的参数都是P类型的继承自Person,L类型继承自Leader,同时还附加了条件(where)这两个类都遵守了TestProtocol协议,由于Leader类型没有遵守TestProtocol协议,条件不满足所以爆红。修改方法则是Leader类遵守TestProtocol。

    参考链接:http://www.jianshu.com/p/a907f0c09a60
    参考链接:http://swift.gg/2015/09/16/swift-generics/

    相关文章

      网友评论

        本文标题:Swift泛型

        本文链接:https://www.haomeiwen.com/subject/aslbattx.html