扩展(extension)
扩展可以给一个类型添加新的功能,但是不能重写已经存在的功能。扩展可以实现:
- 添加计算型实例属性和计算型类属性
- 定义实例方法和类方法 提供新的构造器
- 定义下标
- 定义和使用新的嵌套类型
- 使已经存在的类型遵循一个协议
对一个现有的类型,如果你定义了一个扩展来添加新的功能,那么这个类型的所有实例都可以使用这个新功能,包括那些在扩展定义之前就存在的实例。
计算型属性扩展:
扩展可以添加新的计算属性,但是它们不能添加存储属性,或向现有的属性添加属性观察者。
构造器扩展
扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。
方法扩展:
extension Int {
func repetitions(task: () -> Void){
for _ in 0..<self {
task()
}
}
}
2.repetitions {
print("Hello!")
}
// Hello!
// Hello!
通过扩展添加的实例方法同样也可以修改实例本身。结构体和枚举的方法,若是可以修改 self 或者它自己的属性,则必须将这个实例方法标记为 mutating,就像是改变了方法的原始实现。
可变实例方法扩展
extension Int{
mutating func square(){
self = self * self
}
}
var someInt = 3
someInt.square() //9
扩展可以给现有的类型添加新的下标,
协议
协议语法:
protocol SomeProtocol {
// 这里是协议的定义部分
}
属性要求
get 和set表示可读可写,只读。
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
定义类型属性时,总是使用 static或class 关键字作为前缀。
方法要求
类方法要求使用 class 或 static 作为关键字前缀。
protocol SomeProtocol {
static func someTypeMethod()
}
异变方法要求
在值类型(即结构体和枚举)的实例方法中,将 mutating 关键字作为方法的前缀,写在 func 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。
注: 实现协议中的 mutating 方法时,若是类类型,则不用写 mutating 关键字。而对于结构体和枚举,则必须写 mutating 关键字。
Example:
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle() //on
构造器要求
protocol SomeProtocol {
init(someParameter: Int)
}
构造器要求的类实现
无论是指定构造器还是便利构造器,都必须为构造器实现标上 required 修饰符。使用 required 修饰符可以确保所有子类也必须提供此构造器实现,从而也能遵循协议。
注:如果类已经被标记为 final,那么不需要在协议构造器的实现中使用 required 修饰符,因为 final 类不能有子类。
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 required 和 override 修饰符。
协议作为类型
lass Dice {
let sides: Int
let generator:RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4
委托
定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
在扩展中添加协议遵循
通过扩展令已有类型遵循并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。
有条件的遵循协议
当类型的泛型形式参数遵循对应协议时。可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在采纳协议的名字后面写泛型 where 分句。
在扩展里声明采纳协议
当一个类型已经遵循了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议。
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable {}
协议类型的集合
协议类型可以在数组或者字典这样的集合中使用.
协议的继承
协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔。
类专属协议
通过添加 AnyObject 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者非枚举的类型)。
协议合成
要求一个类型同时遵循多个协议是很有用的。可以使用协议组合来复合多个协议到一个要求里。协议组合行为就和定义的临时局部协议一样拥有构成中所有协议的需求。协议组合不定义任何新的协议类型。
协议组合使用 SomeProtocol & AnotherProtocol 的形式
检查协议一致性
可以使用 类型转换 中描述的 is 和 as 操作符来检查协议一致性,即是否遵循某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的。
- is 用来检查实例是否遵循某个协议,若遵循则返回 true,否则返回 false;
- as? 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 nil;
- as! 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
可选的协议要求
协议可以定义可选要求,遵循协议的类型可以选择是否实现这些要求。在协议中使用 optional 关键字作为前缀来定义可选要求。
可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上 @objc 属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。
使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的,整个函数类型是可选的,而不是函数的返回值。
- 协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。
协议扩展
协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。通过这种方式,可以基于协议本身来实现这些功能,而无需在每个遵循协议的类型中都重复同样的实现,也无需使用全局函数。
提供默认实现
可以通过协议扩展来为协议要求的方法、计算属性提供默认的实现。
通过协议扩展为协议要求提供的默认实现和可选的协议要求不同。虽然在这两种情况下,遵循协议的类型都无需自己实现这些要求,但是通过扩展提供的默认实现可以直接调用,而无需使用可选链式调用。
为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。“where”