swift中协议定义了一个蓝图,规定了用来实现某一些特定人物或者功能的方法,属性,以及其他需要的东西。类,结构体,或枚举都可以遵循协议,并为协议定义的这些要求提供具体的实现。某个类型能够满足某个协议的要求,就可以说改类型遵循这个协议。
除了遵循协议的类型必须实现的要求外,还可以对协议进行扩展,通过扩展来实现一部分需求或者实现一些附加的功能。这样遵循协议的类型就能够使用这些功能。
协议的定义方式与类,结构体和枚举的定义非常相似。
protocol SomeProtocol{
//这里是协议的定义部分。
}
要让自定义类型遵循某个协议,在定义类型是,需在类型名称后面加上协议名称,中间以冒号(:)分隔,遵循多个协议时,各协议之间用逗号(,)分隔
struct SomeStructure:FirstProtocol,AnotherProtocol{
//这里时结构体的定义部分
}
拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔:
class SomeClass:SomeSuperClass,FirstProtocol,AnotherProtocol{
//这里是类的定义部分
}
属性要求
协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型,它指定属性的名称和类型。此外协议还指定属性是可读的还是可读可写的。
如果协议要求的属性是可读可写的,那么改属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。
协议总是用var关键字来声明变量属性,在类型声明后加上{set get} 来表示属性是可读可写的,可读属性则用{get}来表示。
protocol SomeProtocol{
var mustBeSettable:Int{get set}
var doesNotNeedToBeSettable:Int{get}
}
在协议中定义属性时,总是使用static关键字来作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class 关键字来声明类型属性:
protocol AnotherProtocol{
static var someTypeProperty:Int{get set}
}
如下所示,这是一个只含有一个实例属性要求的协议
protocol FullNamed{
var fullName:String{get}
}
FullNamed 协议除了要求遵循协议的类型提供fullName属性外,并没有其他的特别的要求。这个协议表示,任何遵循fullName的类型,都必须有一个可读的String类型的实例属性fullName
下面是一个遵循FullNamed协议的简单结构体
struct Person:FullNamed{
var fullName:String
}
let john = Person(fullName:"John appleseed")
这个例子中定义了一个叫做Person的结构体,用来表示一个具有名字的人。从第一行代码可以看出,它遵循了FullNamed协议。
Person 结构体的每一个实例都有一个String类型的存储属性fullName 。这正好满足了FullNamed协议的要求,也就意味着Person结构体正确的符合了协议。(如果协议要求未被完全满足,在编译时会报错)
下面是一个更为复杂的类,它适配并遵循了FullNamed协议:
class Starship:FullNamed{
var prefix:String?
var name:String
init(name:String,prefix:String? = nil){
self.name = name
self.prefix = prefix
}
var fullName:String{
return(prefix!=nil?prefix!+":") + name
}
}
var ncc1701 = Starship(name:"Enterprise",prefix:"USS")
Starship 类把fullName属性实现为只读的计算型属性。每一个Starship类的实例都有一个名为name的非可选属性和一个名为prefix的可选属性。当prefix存在时,计算属性fullName会将prefix插入到Name之前,从而为星际飞船构建一个全名。
协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同,但是不支持作为协议中的方法的参数提供默认值。
正如属性要求中所述,在协议中定义类方法的时候,总是使用static 关键字作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class 关键字作为前缀:
protocol SomeProtocol{
static func someTypeMethod()
}
下面的例子定义了一个只含有一个实例方法的协议
protocol RandomNumberGenerator{
fun random()->Double
}
RandomNumberGenerator协议要求遵循协议的类型必须拥有一个名为random,返回值为Double的实例方法。尽管这里并未指明,但是我们假设返回值在[0.0,1.0]之间
RandomNumberGenerator协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。