注:来自斯坦福的Swift公开课
协议(Protocol)类似于C++中的一个声明的类,可基本表达为一个方法或变量的清单,但其中不包含任何的实现(a list of methods and vars with no implemention)。
一个协议由三个部分组成:
- 协议的声明(协议中的属性和方法。如函数的参数和返回值,或一个变量的清单)
- class、struct或enum对实现这个协议的声明(要去实现协议中的声明的人,可以是struct、class或enum)
- 上一条中的class、struct或enum中实现的声明的代码(这些代码实现了协议中对变量的处理方法)
在Swift中,一个协议中的所有方法和变量都是强制性的。即如果一个class、struct或enum要实现协议中的方法或变量,则它必须实现协议中的所有方法和变量。但是在OC中不一样,OC中协议可以由可选方法,可以选择实现或者不实现协议中的某些方法和变量。在Swift中可以采用在协议的声明前面添加一个"@ojbc",来表示这是一个OC的协议而不是一个Swift的协议。这样就可以使得协议中的方法变为可选方法(optional)。
声明一个协议的方法:
//SomeProtocol是协议的名字,类似于class A
//InheritedProtocol1, InheritedProtocol2是协议所继承的那些协议
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
//当声明协议中有变量的时候,必须要说明这是一个只读变量还是一个可读写的变量(get,set)
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
//协议中的方法如果会修改实现他的struct,则需要标记为mutating
mutating func changeIt()
init(arg: Type)
}
protocol中的继承和struct中的继承不同。protocol中的继承的意思是,如果声明要实现SomeProtocol,则必须要同时实现InheritedProtocol1和InheritedProtocol2。
如果确定这个协议一定不会被一个struct所实现,就不需要去标记为mutating,但是必须说明这个协议是一个只会被class所实现的协议。可以通过将class放在协议声明后的第一位上,之后这个协议就不能被struct所实现了。例如:
protocol SomeProtocol : class, InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
func changeIt()
init(arg: Type)
}
可以在一个协议当中加上一个初始化(initializer),意味着如果一个class或struct要实现这个协议,它就必须实现这个初始化。
如果一个class或struct要实现一个protocol,只需要在它的父类后面加上它所要实现的protocol(对于struct来说没有父类,只用在后面加上冒号和protocol)。如果声明了要实现一个protocol,但是没有实现里面的方法和变量,编译器就会报错。一个struct或class可以实现任意多的protocol。如果在协议中有初始化(init),则必须要在class或struct中标注required。防止一个子类继承了这个类并且继承了那个初始化,则继承自父类的初始化就会失效。如果标注了required,则子类不会再去实现这个协议,只需要继承父类实现的协议就可以。当一个类实现了一个协议,他的所有子类也能够实现这个协议所以所有的协议的初始化都要标注required。
struct和class中不需要将所实现的protocol中的方法和变量放在自己的结构体或类的声明中,只需要将他们放在extension中。
protocol的使用方法:
protocol Moveable {
mutating func move(to point: CGPoint)
}
class Car : Moveable {
func move(to point: CGPoint) {}
func changeOil()
}
struct Shape : Moveable {
mutating func move(to point: CGPoint) {}
func draw()
}
let prius: Car = Car()
let square: Shape = Shape()
//可以将prius赋值给Moveable型变量thingToMove,因为prius实现了Moveable协议。
var thingToMove: Moveable = prius
//thingToMove不是Car类型的变量,而是Moveable型变量,所以可以调用.move()
//但是不能调用Car中的方法,尽管prius是Car类型的变量并且实现了Car中的方法
thingToMove.move(to:...)
thingToMove.changeOil() //报错
//可以将square赋值给thingToMove,因为square是Shape型,而Shape实现了Moveable类
thingToMove = square
//可以声明一个Moveable类型的数组,里面包含prius和square,即使这是两个不同类型,但是他们都实现了Moveable
let thingsToMove: [Moveable] = [prius, square]
同样protocol类型的变量可以作为函数的参数,例如:
func slide(slider: Moveable) {
let positionToSlideTO = ...
slider.move(to: positionToSlideTO)
}
slide(prius)
slide(square)
函数的参数也可以是一个实现了多个协议的参数,例如:
func slipAndSlide(x: Slippery & Moveable)
slipAndSlide(prius) //报错,因为prius虽然实现了Moveable,但是没有实现Slippery
在这个函数中参数x必须同时实现Slippery和Moveable两个协议。