22. 协议

文章目录
  1. 1. 协议语法
  2. 2. 属性要求
  3. 3. 方法要求
  4. 4. 异变方法要求
  5. 5. 构造器要求
    1. 5.1. 类实现协议构造器的要求
    2. 5.2. 可失败的构造器的要求
  6. 6. 将协议作为类型
  7. 7. 委托
  8. 8. 扩展添加遵循某协议
    1. 8.1. 有条件地遵循协议
    2. 8.2. 通过扩展申明类采纳了协议
  9. 9. 协议类型的集合
  10. 10. 协议继承
  11. 11. 类的专属协议
  12. 12. 协议组合
  13. 13. 检查协议的遵循情况
  14. 14. 可选协议要求
  15. 15. 协议扩展–协议上创建扩展
    1. 15.1. 提供默认实现
    2. 15.2. 为协议扩展添加条件约束
  1. Protocol Syntax
  2. Property Requirements
  3. Method Requirements
  4. Mutating Method Requirements
  5. Initializer Requirements
  6. Protocols as Types
  7. Delegation
  8. Adding Protocol Conformance with an Extension
  9. Collections of Protocol Types
  10. Protocol Inheritance
  11. Class-Only Protocols
  12. Protocol Composition
  13. Checking for Protocol Conformance
  14. Optional Protocol Requirements
  15. Protocol Extensions

协议可以作为方法、属性或者其他的一些特定的任务和功能块的设计蓝图。协议可以适用于类、结构体、枚举,并为它们提供具体的实现或满足特定的需求。任意类型只要满足一个协议的要求,那么我们便称这个类型遵循这个协议。

除了要求遵循协议的类型必须提供对应的实现以外,还可以通过协议扩展来为协议的遵循者提供默认的或者对其有利的实现。

协议语法

1
2
3
protocol SomeProtocol {
// 协议的定义写在这⾥
}
1
2
3
struct SomeStructure: FirstProtocol, AnotherProtocol { 
// 结构体的定义写在这⾥
}

需要注意的是,在为子类适配协议时,父类的名称需要写在协议名之前,分隔符不变。

属性要求

协议可以要求遵循者提供特定名称的实例属性、类型属性。协议只指定属性的名称和类型,而不指定属性是储存属性还是计算属性。此外,协议中也可以指定属性是可读的还是可读可写的。

协议可以指定属性的名称、类型、读写性

协议属性通常会以var关键字来声明变量属性。在类型声明后加上{ get set }来表示属性是可读可写的,用{ get }来表示可读属性。

在协议中定义类型属性时,始终使用static关键字作为前缀。即使该类型属性在类实现时以以 class 或 static 关键字作为前缀,这个规则也适用。

方法要求

我们无法为协议中定义的方法的参数指定默认值。

类型属性一样,当在协议中定义类型方法时,始终使用static 关键字作为前缀。即使该类型方法要求在由类实现时以 class 或 static 关键字为前缀,也是如此。

异变方法要求

注意:如果将协议实例方法要求标记为mutating,则在为类编写该方法的实现时,不需要写mutating关键字。 mutating关键字仅由结构体和枚举使用。

构造器要求

协议可能要求通过遵循类型来实现指定构造器。和普通构造器写法一样,你可以将构造器定义写在协议中,只是不用写大括号和构造器实现。

类实现协议构造器的要求

你可以通过实现指定构造器、便利构造器来使遵循协议的类满足协议的构造器要求。在这两种情况下,你必须使用 required 修饰符标记构造器实现。

可失败的构造器的要求

遵循类型可以用可失败或非可失败的构造器来满足可失败的构造器要求。非可失败的构造器要求必须用非可失败的构造器或隐式展开的可失败的构造器来满足。

将协议作为类型

协议本身并不实现任何功能。不过,你创建的任何协议都可以变为一个功能完备的类型在代码中使用。因为它是一种类型,所以你可以在允许其他类型的许多地方使用协议,包括:

  1. 作为函数、方法或构造器的参数类型或返回类型
  2. 作为常量、变量或属性的类型
  3. 作为数组、字典或其他容器的元素类型

委托

委托是一种设计模式,它使类或结构体能够将其某些职责交给(或委托)到另一种类型的实例。通过定义封装委托职责的协议来实现此设计模式,从而保证遵循协议的类型(称为委托)提供被委托的功能。

扩展添加遵循某协议

extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

有条件地遵循协议

泛型类型可能只能在特定条件下满足协议的要求,例如类的泛型参数遵循一个协议。你可以通过在扩展类型时列出条件约束,让泛型类型有条件的遵循一个协议。通过编写一个泛型where分句,在遵循的协议名称后面写上约束条件。

通过扩展申明类采纳了协议

如果一个类型已经满足遵循一个协议的所有要求,但它没有申明遵循了这个协议,你可以通过一个空的扩展遵循该协议。

注意:类型不会自动遵循一个协议,即便已经满足这个协议的要求。它们必须显示的申明它们遵循了这个协议。

协议类型的集合

协议可以用作诸如数组或字典之类的集合类型的元素类型。

协议继承

协议可以继承一个或多个协议,并且可以在其继承的协议的基础上添加更多的要求。协议继承的语法类似于类继承的语法,但是协议继承支持同时继承多个协议,并用逗号隔开。

类的专属协议

你可以通过将AnyObject协议添加到协议的继承列表中,来将协议限定为仅类类型(而不是结构体或枚举)可用。

注意: 当协议的要求遵循者必须符合引用语义而不是值语义时,请使用类专属协议

协议组合

某些场合下,要求类型可以同时遵循多个协议是很有用的。您可以使用协议组合将多个协议组合到单个需求中。协议组合的行为就像你定义了一个临时本地协议,该协议具有组合中所有协议的要求。协议组合不定义任何新的协议类型。

协议组合使用SomeProtocol & AnotherProtocol的形式。你可以根据需要,列出尽可能多的协议,用&符号分隔它们。除了协议列表之外,协议组合还可以包含一个类类型,你可以使用它来指定继承的父类。

检查协议的遵循情况

你可以使用类型转换中描述的 is 和 as 运算符来检查协议遵循、转换成特定协议。检查和转换协议与检查和转换类型的语法相同:

  1. 如果实例遵循协议,则is运算符返回true ,如果不遵循则返回false 。
  2. 向下转换运算符as?返回协议类型的可选值,如果实例不遵循该协议,则该值为nil 。
  3. 向下转换运算符的as!强制向下转换为协议类型,如果向下转换不成功则触发运行时错误。

可选协议要求

你可以为协议定义可选要求,这些要求不强制遵循者必须实现。可选要求以 optional修饰符为前缀,作为协议定义的一部分。

可选要求允许你的代码与Objective-C交互。协议和可选要求都必须用@objc 属性标记。请注意:@objc协议只能由继承自Objective-C类或其他@objc类的类遵循。结构体或枚举不能遵循它们。

在可选要求中使用方法或属性时,其类型将自动变为可选。例如,类型 (Int) ->String 的方法变为 ((Int) -> String)? 。请注意:整个函数类型变成了可选项, 而不是方法的返回值。

考虑到遵循协议的类型可能未实现要求,你应该使用可选链来调用可选协议。你通过在调用方法名称后面写一个问号来检查可选方法是否实现,例如 someOptionalMethod? (someArgument)

协议扩展–协议上创建扩展

通过在协议上创建扩展,所有遵循者都将自动获得此方法的实现,而无需任何其他修改。

协议扩展可以为符合协议的类型添加实现,但无法扩展协议本身或是继承其他协议。 协议继承始终在协议自身的声明中指定。

提供默认实现

你可以使用协议扩展来为任何方法或计算属性提供默认实现。如果一个遵循者本身就实现了协议中要求的方法或属性,那么这个实现会代替协议扩展中的默认实现。

注意:由协议扩展提供默认实现和可选协议不同。尽管符合的类型不需要提供任何一种协议的实现,但默认的实现在被调用时不需要可选链。

为协议扩展添加条件约束

当我们定义一个协议扩展时,我们可以通过where关键字在被扩展的协议名称后指定一个约束条件。

注意:如果一个类型遵循了多个具有同名方法或属性的扩展协议,那么 Swift会优先调用约束条件较多一方的属性或方法。