协议定义了适合某个特定任务或功能需要的方法、属性和其它需求的一个蓝图。协议本身不提供这些需求的实现,它只是描述了一个任务或功能实现的蓝图。
协议与java 语言中的接口定义类似,都是描述了一个实现可以干什么,而本身却不包含任何实现。与接口不同的是swift语言定义的协议可以被一个类、结构、或者枚举采用,来提供协议规定需求的实际实现,而java 语言的接口只能被类实现。
满足一个协议需求的任何类型(即实现了该协议规定需求)被称为符合该协议。
协议中规定的需求可以是实例属性、实例方法、类型方法、操作符、下标方法等。
1.1 协议语法
协议的定义语法与类、结构和枚举类型类似,以protocol关键字来指示一个协议,并在大括号中规定协议的每一个需求。
protocol SomeProtocol {
// protocol definition goes here
}
一个采用某些协议的特定类型在定义中声明它采用的协议,语法如下:
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
与接口类似,某个类型可以采用(符合)多个协议。类型采用的每个协议之间用逗号来分割,类型名与协议名之间用冒号分割。
如果一个类有超类,把它的超类名放在它采用的协议名前面,并以一个逗号分割:
class SomeClass: SomeSuperclass,FirstProtocol,AnotherProtocol {
// class definition goes here
}
1.2 协议需求项定义
1) 属性需求
一个协议中的一个属性需求仅对需要属性的属性名和类型进行规定,而不规定这些属性是否是一个存储属性或者是一个计算属性,因此只要一个类型提供一个和该属性需求规定的属性名和类型相同的一个实例属性或者一个类型属性,则该类型就符合该协议规定的这条属性需求。
协议的属性需求也必须规定每个属性是只读的或者是可读写的。
如果一个协议中一个属性需求规定是可读写的,那么该属性需求不能被一个常量存储属性或者一个只读的计算属性满足。如果协议的一个属性需求规定是只读的,那么该属性需求能被任何种类的属性满足。
协议中的属性需求总是作为变量属性被声明(var)。可读写属性通过在类型声明后面添加{ get set }来指示,可读属性用{ get }来指示。协议中的实例属性需求规定如下:
protocol SomeProtocol {
var mustBeSettable:Int {getset }
var doesNotNeedToBeSettable:Int {get }
}
在协议中总是使用class关键字来规定一个类型属性,不管采用该协议的可能是一个结构或枚举,而在该结构或枚举中却是使用static前缀来定义该类型属性,只要该类型属性的其它方面满足协议中对该属性的规定,该属性就是符合协议需求的。
protocol AnotherProtocol {
class var someTypeProperty:Int {getset }
}
2) 方法需求
协议中使用与正常的方法相同的语法来规定一条方法需求,但不允许为方法参数规定默认值。
与属性规定相同,在协议中总是使用class关键字来规定一个类型方法需求,不管采用该协议的一个结构或枚举是使用static前缀来定义该方法,只要该方法的其它方面满足该协议对该方法的规定,其方法就是符合协议需求的。
协议的一条方法需求规定如下所示:
protocol SomeProtocol {
class fund someTypeMethod()
}
3) 变异方法需求
不管采用协议的是一个类,还是一个结构或枚举,协议总是使用mutating关键字来标示一个方法是变异方法。一个采用该协议的类的方法不使用mutating关键字来标示。
如下所示,协议中规定了一条变异方法需求。
protocol Togglable {
mutating functoggle()
}
4)协议中的选项需求
你也能在协议中定义选项需求,这些需求不需要被采用该协议的类型实现(可以实现,也可以不实现)。
选项需求使用@optional关键字来标示。
你可以使用选项链来检查一个选项需求是否被采用该协议的类型实现。
选项属性需求、返回一个值的选项方法需求在它们被存取或调用时总是返回一个适当类型的一个选项。
选项需求仅能够在一个使用@objc 属性进行标记的协议中规定,另外使用@objc 属性进行标记的协议仅能被类所采用,而不能被结构或枚举采用。
如下例子展示了协议中选项需求的定义,以及如何使用选项链来使用协议中规定的选项:
@obj cprotocol CounterDataSource {
@optional func incrementForCount(count:Int) ->Int
@optional var fixedIncrement:Int {get }
}
该协议规定了一条选项方法,一条选项属性。
@objc class Counter {
var count =0
var dataSource:CounterDataSource?
func increment() {
if let amount =dataSource?.incrementForCount?(count) {
count +=amount
}else if let amount =dataSource?.fixedIncrement? {
count +=amount
}
}
}
使用该协议的类Counter也用@objc标记,在该类中使用选项链来读取和调用协议中规定的选项属性和选项方法。
1.3 协议的类型
协议是一种类型。因此可以使用协议作为一个函数、方法或初始化方法中的参数类型或者返回类型;也可以作为一个常量、变量或属性的类型;也可以作为一个数组、词典或其它容器中的项的类型。
由于协议是类型,因此它也能够使用is 和as操作符来检查协议的兼容性,可以使用is 操作符来检查一个实例是否采用了某个协议,使用as操作符来转换一个协议到它的继承链上的另外的协议。
与协议的选项需求规定类似,使用以上操作来检查协议的兼容性也需要协议使用@objc 属性进行标记,另外使用@objc 属性进行标记的协议仅能够被类采用,而不能被结构或枚举采用。
1.4 用扩展来采用协议
能够使用扩展来扩展某个类型(即使已经存在),使该类型采用和符合一个新协议。在扩展中实现新协议规定的方法、属性和下标。
扩展后的类型扩展前的已存在的实例也自动获得协议规定的能力。
语法如下:
extension ExistType:NewProtocol {
// protocol implementation goes here
}
一个已经实现某个协议所有需求的类型如果不明确声明采用该协议,那么它不自动采用那个协议。一个类型采用某个协议,必须被声明。
可以使用一个空的扩展来明确地声明已实现某个协议所有需求的类型采用这个协议。
extension ExistType: NewProtocol {}
1.5 协议的继承
与接口允许继承相同,协议也能够继承。
协议能够继承一个或多个其它的协议,使一个协议在它继承的协议基础上增加进一步的需求。协议继承的语法如下:
protocol InheritingProtocol:SomeProtocol,AnotherProtocol {
// protocol definition goes here
}
1.6 协议的复合
在使用协议作为类型使用时,你能够组合多个协议作为一个临时类型使用,语法为:
protocol<SomeProtocol, AnotherProtocol>,在三角括号内列出要组合的多个协议,每个协议间用逗号分割。
如下例子展示了使用协议组合作为一个函数参数类型的用法。
protocol Named {
var name:String {get }
}
protocol Aged {
var age:Int {get }
}
struct Person:Named,Aged {
var name:String
var age:Int
}
func wishHappyBirthday(celebrator:protocol<Named,Aged>) {
println("Happy birthday\(celebrator.name) - you‘re \(celebrator.age)!")
}
let birthdayPerson =Person(name:"Malcolm",age:21)
wishHappyBirthday(birthdayPerson)
协议复合没有定义一个新的协议,而只是临时组合多个协议作为一个临时协议类型使用。
版权所有,请转载时清楚注明链接和出处,谢谢!