协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西。类、结构体或枚举都可以采纳协议,并为协议定义的这些要求提供具体实现。某个类型能够满足某个协议的要求,就可以说该类型“符合”这个协议。
除了采纳协议的类型必须实现的要求外,还可以对协议进行扩展,通过扩展来实现一部分要求或者实现一些附加功能,这样采纳协议的类型就能够使用这些功能。
属性要求
1、协议总是用var关键字来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,可读属性则用{ get }来表示
2、在协议中定义类型属性时,总是使用static关键字作为前缀。当类类型采纳协议时,除了static关键字,还可以使用class关键字来声明类型属性
⚠️类型属性 可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是类型属性
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
protocol AnotherProtocol { staticvarsomeTypeProperty: Int {get set}}
⚠️如果协议要求未被完全满足,在编译时会报错
方法要求
1、协议可以要求采纳协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。
正如属性要求中所述,在协议中定义类方法的时候,总是使用static关键字作为前缀。当类类型采纳协议时,除了static关键字,还可以使用class关键字作为前缀
2、有时需要在方法中改变方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将mutating关键字作为方法的前缀,写在func关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。
3、协议可以要求采纳协议的类型实现指定的构造器。你可以在采纳协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器。无论哪种情况,你都必须为构造器实现标上required修饰符
⚠️如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注required和override修饰符
协议作为类型
尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来使用。
协议的类型的实例都可以赋值
委托(代理)模式
委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保采纳协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
通过扩展添加协议一致性
即便无法修改源代码,依然可以通过扩展令已有类型采纳并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。
通过扩展采纳协议
当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空扩展体的扩展来采纳该协议
协议的继承
协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔
类类型专属协议
你可以在协议的继承列表中,通过添加class关键字来限制协议只能被类类型采纳,而结构体或枚举不能采纳该协议。class关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol{ // 这里是类类型专属协议的定义部分 }
协议合成
有时候需要同时采纳多个协议,你可以将多个协议采用SomeProtocol & AnotherProtocol这样的格式进行组合,称为协议合成
检查协议一致性
你可以使用类型转换中描述的is和as操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换完全相同:
is用来检查实例是否符合某个协议,若符合则返回true,否则返回false。
as?返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回nil。
as!将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。
可选的协议要求
协议可以定义可选要求,采纳协议的类型可以选择是否实现这些要求。在协议中使用optional关键字作为前缀来定义可选要求。
协议扩展
协议可以通过扩展来为采纳协议的类型提供属性、方法以及下标的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个采纳协议的类型中都重复同样的实现,也无需使用全局函数。
通过协议扩展,所有采纳协议的类型,都能自动获得这个扩展所增加的方法实现,无需任何额外修改,如果采纳协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用where子句来描述