协议:规定了用来实现某一特定 工作
或者 功能
所必须的 方法 和 属性。
类、结构体,枚举 都可以遵循协议。(协议要求类,结构体,枚举必须实现协议要求的 属性 和 方法。)
任意能够满足协议要求的类型被称为 遵循
这个协议。
协议除了遵守协议还可以进行协议扩展,来实现一些附加的特殊功能。
协议语法:
protocol SomeProtocol {
// 协议内容
}
遵循某个协议
遵循某个协议,需要在类型名称后面加上协议名称,中间以冒号:
, 作为类型定义的一部分。
struct SomeStructure : FistProtocol , AnotherProtocol {
// 结构体的内容( 属性 和 方法)
}
类在遵守协议的同时拥有父类,父类名放在协议的前面,以逗号进行分隔。
class SomeClass: SomeSuperClasses, FistProtocol , AnotherProtocol {
// 类的内容
}
协议对属性的规定
协议可以规定其 遵守者 提供特定名称和类型的 实例属性(instance property) 或者 类属性(type property)。
协议是不能指定 属性 是 **存储属性 ** 还是 ** 计算属性 **。
协议可以指定 属性 是 可读的 还是 ** 可读可写的 **。
- 协议要求属性是可读可写的,那么属性不能是 常量属性 或 只读的计算型属性
如果协议只 要求属性是可读的,那么这个属性 不仅 是可读的,可以通过代码修改为可读可写的。
协议总是用 var 关键字来声明 变量属性,在类型名称后加上 { set get } 来表示属性是可读可写的,用 { get } 来表示可读:
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doseNotNeedToBeSettable: Int { get }
}
- 类型属性的定义: 协议中总是使用 ** static ** 关键字作为前缀。当类类型采纳协议时,除了 使用 static 关键字,还可以使用 class 关键字来声明类属性:
protocol AnotherProtocol {
// 类型属性
static var someTypeProperty: Int { get set }
}
protocol FullNamed {
// 实例属性
var fullName: String { get }
}
协议对于方法的规定
协议要求采纳协议的类型实现某些指定的 实例方法 或 类方法。
- 可以在协议中定义具有可变参数的方法(和普通方法的定义一样)
不支持为协议中的方法的参数提供默认值
协议中定义方法,使用 static关键字作为前缀。 当类类型采纳协议时,除了 使用 static 关键字,还可以使用 class 关键字最为前缀
protocol SomeProtocol {
// 类型方法
static fun someTypeMethod()
// 实例方法
fun random() -> Double
}
Mutating 关键字的使用
Mutating 主要是用在值类型(结构体 和 枚举)实例方法中。
将 mutating 关键字作为方法的前缀,写在 func 关键字之前。表示 可以在该方法中修改他所属的实例以及实例的任意属性值。
结构体 和 枚举 是值类型,默认情况下值类型的属性是不能再他的实例方法中进行修改。
在协议中定义实例方法的时候,当方法会改变采纳该协议的类型的实例的时候,在定义协议方法的时候需要在方法前加 mutating 关键字。(主要是给枚举和结构体准备的)
在协议中定义了实例方法,如果遵协议的类型是 枚举 或 结构体,那么就需要在协议方法的前面添加 mutating 关键字。
构造器要求
协议可以要求采纳协议的类型实现指定的 构造器。
protocol SomeProtocol {
init(someParamer: Int)
}
构造器要求在类中的实现
采纳协议的类实现构造器,无论是指定构造器,还是便利构造器。都需要在构造器实现上 ** required ** 修饰符:
class SomeClass: SomeProtocol {
required init(someParameter:Int){
// 构造器实现
}
}
可选协议
在协议中使用 optional 关键字作为前缀来定义可选要求。
使用可选要求时,可选的方法或函数的类型会自动变为可选。
** 是整个函数的类型是可选的 **
协议合成
当需要同时采纳多个协议,我们可以使用协议合成。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>) {
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// 打印 “Happy birthday Malcolm - you're 21!”
协议扩展
协议可以通过 扩展 来为采纳协议的类型提供属性、方法以及下标的实现。
你可以基于协议本身来实现这些功能, 而无需再每个采纳协议的类型中都重复同样实现。也无需使用全局函数。
extension RandomNumberGeneraator {
func randomBool() -> Bool {
return random() > 0.5
}
}
** 通过协议扩展,所有采纳协议的类型,都能自动获得这个扩展所增加的方法实现,无需任何修改。 **
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And here's a random Boolean: \(generator.randomBool())")
// 打印 “And here's a random Boolean: true”
** 提供默认实现 **
协议可以通过 扩展 来为采纳协议的类型提供属性、方法以及下标提供 默认 的实现
采纳协议的类型提供了自己的实现,那么自定义的实现将会替代扩展中的默认实现。
通过协议扩展为协议要求提供的默认实现和可选的协议要求不同: