22.1 协议的语法
说明:协议的定义方式与
类
,结构体
,枚举
的定义非常相似。
- 要使类遵循某个协议,需要在
类型名称后
加上协议名称
,中间以:
分隔,作为类型定义的一部分- 遵循多个 协议时,各协议之间用逗号
,
分隔- 如果类在遵循协议的同时拥有父类,应该将
父类名
放在协议名之前
,以,
分隔
1 | // 定义协议 |
22.2 对属性的规定
说明:表现在
4
个方面
- 名称
- 类型
- 可读还是可写
- 实例属性还是类属性
注意:不指定是
存储型属性
还是计算型属性
1 | // 定义协议 |
22.2.1 可读&可写
说明:在协议中指定属性规则是都使用
var
(不使用let
)
可读&可写 | 协议 | 实现 |
---|---|---|
可读 | {get} |
可以只读,可以可读可写 |
可写 | {get set} |
不能是常量 或只读计算属性 |
1 | // 定义协议 |
22.2.2 实例属性&类属性
说明:
实例属性&类属性 | 协议 | 实现 |
---|---|---|
实例属性 | 默认 | 默认 |
类属性 | static |
static 或class |
1 | // 定义协议 |
22.3 对方法的规定
说明:协议可以要求采纳协议的类型实现某些指定的
实例方法
或类方法
。
语法:像普通方法一样放在协议的定义中,但是不需要{}
和方法体
- 可以在协议中定义具有
可变参数
的方法,和普通方法的定义方式相同不支持
为协议中的方法的参数提供默认值- 在协议中定义类方法的时候,总是使用
static
关键字作为前缀;当类类型采纳协议时,除了static
关键字,还可以使用class
关键字作为前缀
1 | // 定义协议 |
22.4 对Mutating方法的规定
说明:如果你在
协议中
定义了一个实例方法
,该方法会改变采纳该协议的类型的实例
,那么在定义协议时需要在方法前加mutating
实现者 | 协议 | 实现 |
---|---|---|
类类型 | mutating |
不用写 mutating 关键字 |
值类型 | mutating |
必须写mutating 关键字 |
1 | // 协议 |
22.5 对构造器的规定
说明:协议可以要求采纳协议的类型实现指定的构造器
语法:你可以像编写普通构造器那样,在协议的定义里写下构造器的声明
,但不需要写{}和构造器的实体
22.5.1 构造器要求在类中的实现
说明:使用
required
修饰符(除非在final
类中),可以是指定构造器
或便利构造器
- 如果一个子类
重写
了父类的指定构造器,并且该构造器满足了某个协议
的要求,那么该构造器的实现需要同时标注required
和override
修饰符- 如果类已经被标记为
final
,那么不需要在协议构造器的实现中使用required
修饰符,因为final
类不能有子类注意:使用
required
修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。
1 | // 协议 |
22.5.2 可失败构造器要求
说明:协议还可以为采纳协议的类型定义
可失败构造器要求
构造器 | 协议 | 实现 |
---|---|---|
可失败构造器 | init? |
init? 或init |
非可失败构造器 | init |
init 或init! |
22.6 协议作为类型
说明:尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来使用。
- 作为函数、方法或构造器中的
参数类型
或返回值类型
- 作为
常量
、变量
或属性
的类型- 作为数组、字典或其他
容器中的元素类型
注意:作为一种类型,协议类型的名称应与其他类型的写法相同(
大写字母开头的驼峰式写法
)
1 | // 协议 |
22.7 委托(代理)模式
描述:委托是一种
设计模式
,它允许类或结构体
将一些需要它们负责的功能委托给其他类型的实例。
说明:委托模式的实现很简单
- 定义协议来封装那些需要被委托的功能,这样就能确保采纳协议的类型能提供这些功能
- 委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
案例:
游戏
将监控这一功能委托给游戏监控器
。
1 | // 协议 |
22.8 在扩展中添加协议成员
说明:即便无法修改源代码,依然可以通过扩展令已有类型采纳并符合协议。
原理:通过扩展令已有类型采纳并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。
1 | // 协议:要求提供一个能描述自身的计算属性 |
22.9 通过扩展补充协议声明
说明:可以通过扩展
空的扩展体
补充协议声明。需要两个前提
已经实现了
协议中所有要求没有声明
为遵循该协议
1 | // 协议:要求提供一个能描述自身的计算属性 |
22.10 集合中的协议类型
说明:协议类型可以在集合使用,表示
集合中的元素均为协议类型
注意:被当作协议类型使用后,只能使用和访问协议指定的功能(丢失具体类型的特色能力)。
1 | // 协议类型数组 |
22.11 协议的继承
说明:协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。和类的继承类似
语法:多个被继承的协议间用,
分隔
1 | protocol InheritingProtocol: SomeProtocol, AnotherProtocol { |
1 | // 父类协议 |
22.12 类类型专属协议
说明:以在
协议的继承列表
中,通过添加class
关键字来限制协议只能被类
类型采纳,而结构体
或枚举
不能采纳该协议
语法:class
关键字必须第一个出现在协议的继承列表中
(在其他继承的协议之前)
应用:当协议定义的要求需要采纳协议的类型必须是引用
语义而非值
语义时,应该采用类类型专属协议。
1 | protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { |
22.13 协议合成
说明:有时候需要同时采纳多个协议,你可以将多个协议采用
protocol<SomeProtocol, AnotherProtocol>
这样的格式进行组合,作为一种临时协议
存在,这个过程称为协议合成
。
语法:可以在protocol<>
中罗列任意多个你想要采纳的协议,以,
分隔。
注意:协议合成并不会生成新的、永久的协议类型,而是将多个协议中的要求合成到一个只在局部作用域有效
的临时协议
中。
1 | // 协议1 |
22.14 检验协议的一致性
说明:其实就是检验
是否符合某协议
,或者转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换
完全相同
操作符 | 用途 | 返回值 | 备注 |
---|---|---|---|
is |
用来检查实例是否符合某个协议 | 符合则返回 true ,否则返回 false |
|
as? |
返回指定协议的可选值 | ,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 nil |
|
as! |
将实例强制向下转换到某个协议类型 | 指定的协议类型 | 如果强转失败,会引发运行时错误 |
1 | // 协议 |
22.15 对可选协议的规定
说明:协议可以定义可选要求,采纳协议的类型可以选择是否实现这些要求。
语法:在协议中使用optional
关键字作为前缀来定义可选要求。
- 使用可选要求时,它们的类型会自动变成可选的
- 需要
import Foundation
限制:
- 定义协议:可选的协议要求只能用在标记
@objc
特性的协议中- 采纳协议:标记
@objc
特性的协议只能被继承自 Objective-C 类的类
或者@objc 类
采纳
1 | import Foundation |
22.16 协议扩展
说明:协议可以通过
扩展
来为采纳协议的类型
提供属性、方法以及下标脚本的实现。
优点:扩展协议不但扩展了要求,同时提供了实现。因而无需
在每个采纳协议的类型中都重复同样的实现,也无需
使用全局函数。
1 | // 定义协议 |
22.16.1 提供默认实现
说明:可以通过
协议扩展
来为协议要求的属性、方法以及下标脚本提供默认的实现
。
注意:如果采纳协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现
被使用。
1 | // 扩展协议 |
22.16.2 为协议扩展添加限制条件
说明:在
扩展协议
的时候,可以指定一些限制条件
,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现
。
语法:这些限制条件写在协议名之后
,使用where
子句来描述.
1 | // 协议:要求提供一个能描述自身的计算属性 |