Swift 协议

前言

  • 协议用于统一方法和属性的名称,但是协议没有实现,在其它语言中通常叫做接口。

    • 协议也是一种数据类型,就像类、结构体和枚举那样的数据结构,可以把它当作参数。
    • 协议也可以是一个常量或变量,唯一的区别是协议本身没有实现,它只有声明,实现由其它遵守协议的对象来实现。
    • 可以提前在协议扩展中定义协议的默认实现,不过这些默认实现并不属于协议本身。
  • 使用协议的好处

    • 一是只实现了该实现的,子类只知道了该知道的。
    • 其次,协议也可以作为数据类型来传输,这样在传递数据时,我们不用关注对象的类型,提高了抽象层次和通用性,也就是可复用性。
  • 协议的使用步骤

    • 1)声明协议,很像其它数据类型的声明,只不过没有实现而已。
    • 2)扩展协议(可选步骤),可以指定扩展的适用对象,在扩展中定义默认的实现。
    • 3)遵守协议,有类、结构体或者枚举表示遵守这个协议。
    • 4)实现协议,遵守协议的数据类型来实现协议中声明的属性和方法,改写获得的默认实现。

1、声明协议

  • Swift 语言中协议的声明使用关键字 protocol

    protocol 协议: 继承的协议1, 继承的协议2 {
    
        var 某个属性: 类型 {set get}
        func 某个方法(参数列表) -> 返回值类型
        init 构造器(参数列表)
    }
    
  • 1)在协议中加入的属性可以不用实现,也不限制于是计算属性还是存储属性,但是必须要指出属性的读写权限。

    • {set get} 表示可读写,{get} 表示可读。

    • 当在实现时为可读属性添加 setter 方法,系统也不会报错,协议中指定的权限只是最低权限。

      // 定义协议
      protocol someProtocol {
      
          var num: Int {get}
      }
      
      class Show: someProtocol {
      
          var so = 1
      
          // 实现协议中定义的属性
          var num: Int {
              get {
                  return so
              }
              set {
                  so = newValue + 1
              }
          }
      }
      
      var show1 = Show()
      show1.num = 1
      print(show1.num)            // 2
      
  • 2)类、结构体和枚举都可以遵守协议。

    • 如果是结构体和枚举类型遵守的协议方法中有对属性的改动,那么按照规定,在协议中声明这个方法时需要将方法指定为变异方法

    • 如果是类遵守了协议,那么协议中的变异方法和普通方法没有区别。

      mutating func 变异方法名()
      
  • 3)可以在协议的定义中指定某个成员为类型成员,在成员定义前加上关键字 static 即可。

    protocol someProtocol {
    
        // 定义类型成员
        static func someTypeNethod()
    }
    
  • 4)限制协议仅和类一起工作也是可行的,只需要在冒号后面添加一个 class 关键字,这样就代表这个协议只能被类所遵守。

    protocol 协议: class, 继承的协议1, 继承的协议2 {
    
        var 某个属性: 类型 {set get}
        func 某个方法(参数列表) -> 返回值类型
        init 构造器(参数列表)
    }
    

2、遵守协议

  • Swift 中遵守协议的格式和继承父类一样,把协议名放到类声明的尾部,在继承的后面,以逗号隔开。

    • 一个类只能继承一个父类,但是可以遵守多个协议。

      calss 某个类: 父类, 协议1, 协议2, ... {}
      
  • 协议别名

    • 如果多个协议总是一起出现,则可以使用 typealias 关键字给多个协议起一个别名,typealias 并不会生成新的协议。

      // Swift 3.0 以前
      typealias 协议组合别名 = protocol<协议1, 协议2, ...>
      
      // Swift 3.0 以前
      typealias 协议组合别名 = 协议1 & 协议2 ...
      
      calss 某个类: 父类, 协议组合别名 {}
      
      struct 某个结构体: 协议组合别名 {}
      

3、实现协议

  • 一旦类遵守了这个协议,就必须实现它里面的所有成员,不然无法通过编译,结构体和枚举也是如此。

  • 如果类遵守的协议中声明了构造器,那么遵守协议的类在实现这个构造器的时候必须把构造器声明为 required,否则根据构造器的继承原则,可能导致子类没有实现该构造器的情况。

4、协议扩展

  • Swift 2.0 之后苹果宣称 Swift 是一门 “面向协议编程” 的语言,这是由于 Swift 2.0 中引入了对协议扩展的特性。

    • 由于 Swift 是单类继承,并且结构体和枚举还不能被继承,因此对很多有用信息的传递造成了一定的麻烦。
    • 扩展协议的好处是,类、结构体和枚举都可以遵守不止一个协议。
    • 并且遵守协议不会增加类的状态。

4.1 定义协议属性的默认值

  • 可以提前在协议扩展中定义协议的默认实现,不过这些默认实现并不属于协议本身。

  • 定义两个协议 Coder 和 Swifter。

    protocol Coder {
        var haveFun: Bool {get}
        var ownMoney: Bool {get}
    }
    
    protocol Swifter {
        var codingLevel: Int {get}
    }
    
  • 现在有三个公司的程序员,用三个结构体来表示。

    struct CoderFromA: Coder {
    
        var haveFun: Bool = false
        var ownMoney: Bool = false
    
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    struct CoderFromB: Coder, Swifter {
    
        var haveFun: Bool = true
        var ownMoney: Bool = true
    
        var codingLevel: Int = 3
    
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    struct CoderFromC: Coder, Swifter {
    
        var haveFun: Bool = true
        var ownMoney: Bool = true
    
        var codingLevel: Int = 5    
    
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    // 使用
    
    let coderA = CoderFromA(name: "A")
    print("(coderA.name) - (coderA.haveFun) - (coderA.ownMoney)")
    // A - false - false
    
    let coderB = CoderFromB(name: "B")
    print("(coderB.name) - (coderB.haveFun) - (coderB.ownMoney) - (coderB.codingLevel)")
    // B - true - true - 3
    
    let coderC = CoderFromC(name: "C")
    print("(coderC.name) - (coderC.haveFun) - (coderC.ownMoney) - (coderC.codingLevel)")
    // C - true - true - 5
    
    • 所有程序员都关心自己是否快乐、是否有钱,所以每个结构体都遵守协议 Coder
    • A 公司的程序员不是 Swift 程序员,而 B 和 C 公司的程序员都是 Swift 程序员,每个公司的 Swift 程序员的编程能力等级不同。
  • 观察上面的代码可以发现 Swift 程序员都是快乐且富有的,因此结构体 CoderFromBCoderFromC 中会有冗余的部分,这是由于不同的协议间的因果关系造成的,虽然我们知道这个事实,但是由于规则的关系我们不得不重复的去赋值 haveFunownMoney 属性。

  • 现在使用 swift 的协议扩展,形式如下。

    // 定义协议
    
    protocol Coder {
        var haveFun: Bool {get}
        var ownMoney: Bool {get}
    }
    
    protocol Swifter {
        var codingLevel: Int {get}
    }
    
    // 定义协议扩展,设置默认值
    
    extension Coder where Self: Swifter {
    
        var haveFun: Bool {
            return true
        }
    
        var ownMoney: Bool {
            return true
        }
    }
    
    // 定义遵守协议的类型
    
    struct CoderFromA: Coder {
    
        var haveFun: Bool = false
        var ownMoney: Bool = false
    
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    struct CoderFromB: Coder, Swifter {
    
        //var haveFun: Bool = true
        //var ownMoney: Bool = true
    
        var codingLevel: Int = 3
    
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    struct CoderFromC: Coder, Swifter {
    
        //var haveFun: Bool = true
        //var ownMoney: Bool = true
    
        var codingLevel: Int = 5
    
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    // 使用
    
    let coderA = CoderFromA(name: "A")
    print("(coderA.name) - (coderA.haveFun) - (coderA.ownMoney)")
    // A - false - false
    
    let coderB = CoderFromB(name: "B")
    print("(coderB.name) - (coderB.haveFun) - (coderB.ownMoney) - (coderB.codingLevel)")
    // B - true - true - 3
    
    let coderC = CoderFromC(name: "C")
    print("(coderC.name) - (coderC.haveFun) - (coderC.ownMoney) - (coderC.codingLevel)")
    // C - true - true - 5
    
    • 协议扩展中使用 where 限定 Coder 协议的遵守者在同时遵守 Swifter 协议的时候可以获得本次扩展中的默认实现。
    • 现在当某个数据结构同时遵守 CoderSwifter 时,协议 Coder 中的属性 haveFunownMoney 会有默认值。
    • 注意协议扩展中不能定义存储属性,所以这里的 haveFunownMoney 的值以计算属性 get 方法的形式返回。
    • 此时可以删除 CoderFromBCoderFromChaveFunownMoney 的声明。

4.2 定义协议方法的默认实现

  • 在协议的扩展中,除了给协议中定义的方法赋上默认实现外,还可以定义新的方法并赋上默认实现。

  • 1)常规的写法

    // 定义协议
    
    protocol SharedString {
        func methodForOverride()
        func methodWithoutOverride()
    }
    
    // 定义协议扩展,实现方法
    
    extension SharedString {
    
        func methodForOverride() {
            print("method For Override")
        }
    
        func methodWithoutOverride() {
            methodForOverride()
        }
    }
    
    // 使用
    
    extension String: SharedString {
    
    }
    
    // String 上下文
    let str1: String = "Hello"
    str1.methodForOverride()         // method For Override
    str1.methodWithoutOverride()     // method For Override
    
    // SharedString 上下文
    let str2: SharedString = "Hello"
    str2.methodForOverride()         // method For Override
    str2.methodWithoutOverride()     // method For Override
    
  • 2)现在在遵守协议的时候重新定义方法 methodForOverride,修改它的实现。

    // 定义协议
    
    protocol SharedString {
        func methodForOverride()
        func methodWithoutOverride()
    }
    
    // 定义协议扩展,实现方法
    
    extension SharedString {
    
        func methodForOverride() {
            print("method For Override")
        }
    
        func methodWithoutOverride() {
            methodForOverride()
        }
    }
    
    // 使用
    
    extension String: SharedString {
    
        func methodForOverride() {
            print(self)
        }
    }
    
    // String 上下文
    let str1: String = "Hello"
    str1.methodForOverride()         // Hello
    str1.methodWithoutOverride()     // Hello
    
    // SharedString 上下文
    let str2: SharedString = "Hello"
    str2.methodForOverride()         // Hello
    str2.methodWithoutOverride()     // Hello
    
  • 3)现在把方法 methodWithoutOverride 的声明从 SharedString 协议列表中删除,将它变成一个从声明到实现都在协议扩展中的方法。

    // 定义协议
    
    protocol SharedString {
    
        func methodForOverride()
    }
    
    // 定义协议扩展,实现方法
    
    extension SharedString {
        
        func methodForOverride() {
            print("method For Override")
        }
    
        func methodWithoutOverride() {
            methodForOverride()
        }
    }
    
    // 使用
    
    extension String: SharedString {
    
        func methodForOverride() {
            print(self)
        }
    }
    
    // String 上下文
    let str1: String = "Hello"
    str1.methodForOverride()         // Hello
    str1.methodWithoutOverride()     // Hello
    
    // SharedString 上下文
    let str2: SharedString = "Hello"
    str2.methodForOverride()         // Hello
    str2.methodWithoutOverride()     // Hello
    
    • 再次在 String 和 ``SharedString两种上下文中调用方法,结果仍旧是一样的,看起来String对协议方法methodForOverride` 的修改是绝对的。
  • 4)最后一步,把方法 methodForOverride 从协议列表中列表中删除,现在 SharedString 的声明列表是空的了。

    // 定义协议
    
    protocol SharedString {
    
    }
    
    // 定义协议扩展,实现方法
    
    extension SharedString {
    
        func methodForOverride() {
            print("method For Override")
        }
    
        func methodWithoutOverride() {
            methodForOverride()
        }
    }
    
    // 使用
    
    extension String: SharedString {
    
        func methodForOverride() {
            print(self)
        }
    }
    
    // String 上下文
    let str1: String = "Hello"
    str1.methodForOverride()         // Hello
    str1.methodWithoutOverride()     // method For Override
    
    print("
    ")
    
    // SharedString 上下文
    let str2: SharedString = "Hello"
    str2.methodForOverride()         // method For Override
    str2.methodWithoutOverride()     // method For Override
    
    • 当两个方法都定义在协议扩展中,并且上下文为 String 时,methodForOverride 会调用被重写的版本,而未被重写的方法 methodWithoutOverride 会调用协议扩展中默认的版本,并且未被重写的方法内部调用协议中其它方法时获得的也是没有被重写的版本,这就是协议扩展的 “静态特性”。
    • 当两个方法都定义在协议扩展中,并且上下文为 SharedString 时,那么两个方法都会获得默认的版本。

4.3 动态方法

  • 动态方法的协议方法定义在协议的声明中,会完全被协议遵守者重写,无论你是否在协议扩展中给予协议方法默认实现,获得的协议方法都是被重写过的。

4.4 静态方法

  • 静态方法的协议方法定义在协议的扩展中,当上下文不为协议类型时,协议中定义的方法会完全被协议遵守者重写;当上下文为协议类型时,会完全使用默认实现。

4.5 where 关键字

  • where 的作用是限定协议扩展有效的条件,在 where 语句中可以使用 Self 关键字来代表协议的遵守者,可以指定遵守者是某个类的子类或者遵守了某个协议。

4.6 上下文

  • 在 Swift 的协议世界中,每个对象都可能遵守许多协议,而协议本身可以作为一种类型,在使用类型推断时,编译器会把对象推断为对象本身。

  • 比如说上例中,你可以通过指定实例的类型修改上下文。

5、协议继承

  • 协议本身可以继承自另一个协议,当然不能继承自另一个类,因为协议是不能有具体方法实现的,所以不能继承自类,协议继承自另一个协议,无非就是多了一个方法定义。
  • 协议也有继承关系,如果想要遵守这个协议,就必须把它继承的协议也全部实现。
原文地址:https://www.cnblogs.com/QianChia/p/8659507.html