Swift提供一个叫扩展(extension)的特性,该特性就是为这种情况设计的。扩展能让你给已有的类型添加功能,可以用来扩展结构体、枚举和类,比如:
extension TypeName {}
比如,为系统的Double对象添加一个mph方法,就可以用下列的代码实现
extension Double {
var mph: Double { return self }
}
// 给Double起个别名
typealias Velocity = Double
//添加两个计算属性
extension Velocity {
var kph: Velocity { return self * 1.60934 }
var mph: Velocity { return self }
}
本例子中会以自定义的Car结构体为例进行说明,如何扩展自定义的类型的。
// 自定义类型
struct Car {
let make: String
let model: String
let year: Int
let color: String
let nickname: String
var gasLevel: Double { //可变的存储属性,附带了观察者实现
willSet {
precondition(newValue <= 1.0 && newValue >= 0, "New value must be between 0 and 1.")
}
}
}
在自定义协议添加三个计算属性
protocol Vehicle {
var topSpeed: Velocity { get }
var numberOfDoors: Int { get }
var hasFlatbed: Bool { get }
}
下面的代码其实就是协议实现的另一种写法
// 添加Vehicle协议实现
extension Car: Vehicle {
var topSpeed: Velocity { return 180 }
var numberOfDoors: Int { return 4 }
var hasFlatbed: Bool { return false }
}
Car的成员初始化方法之所以能保留下来,是因为新初始化方法是在扩展中定义和实现的,这个模式很有用。如果用自定义初始化方法的方式会覆盖掉默认的初始化方法
//扩展初始化方法
extension Car {
init(make: String, model: String, year: Int) {
self.init(make: make,
model: model,
year: year,
color: "Black",
nickname: "N/A",
gasLevel: 1.0)
}
}
//测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
//添加嵌入类型
extension Car {
enum Kind { //添加的嵌套类型
case coupe, sedan
}
var kind: Kind { //添加一个名为Kind的计算属性
if numberOfDoors == 2 {
return .coupe
} else {
return .sedan
}
}
}
//测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
c.Kind
//添加函数
extension Car {
mutating func emptyGas(by amount: Double) {
//precondition是一个框架方法
precondition(amount <= 1 && amount > 0, "Amount to remove must be between 0 and 1.")
gasLevel -= amount
}
mutating func fillGas() {
gasLevel = 1.0
}
}
//测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
c.emptyGas(by: 0.3)
c.gasLevel
c.fillGas()
c.gasLevel
OOP中的一个强大但也是一个缺陷功能就是继承,尤其过深的继承层次很容易让代码充满难以理解的类。Swift为设计可重用、可组合的类型带来了新机会:不用类和继承,而是用协议和泛型。即便使用值类型,协议也能解决OOP中继承能解决的问题。协议扩展(protocol extension)是使得这种设计成为可能的强大工具。语法结构如下:
协议扩展:相当于一种变相的继承实现,对应的是objective-C中的概念。
语法结构:protocol Exercise: CustomStringConvertible
Exercise是一个自定义的协议,CustomStringConvertible是一个系统协议
protocol Exercise: CustomStringConvertible {
var name: String { get }
var caloriesBurned: Double { get }
var minutes: Double { get }
}
在上面扩展的CustomStringConvertible协议中包含了一个名为description的函数,必须要实现,这段代码也可以写在上面的实现了,但为了演示协议的扩展,特意分出来一段代码实现,如下:
extension Exercise {
var description: String {
return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"
}
}
自定义两个结构体,实现自定义的协议
//定义结构体EllipticalWorkout,并扩展Exercise协议
struct EllipticalWorkout: Exercise {
let name = "Elliptical Workout"
let title = "Workout using the Go Fast Elliptical Trainer 3000"
let caloriesBurned: Double
let minutes: Double
}
//Exercise(Elliptical Workout, burned 335.0 calories in 30.0 minutes)
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)
//定义结构体TreadmillWorkout,并扩展Exercise协议
struct TreadmillWorkout: Exercise {
let name = "Treadmill Workout"
let caloriesBurned: Double
let minutes: Double
let laps: Double
}
//可以二次扩展,这个方法会覆盖掉原来 Exercise 中的实现
extension TreadmillWorkout {
var description: String {
return "Treadmill(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"
}
}
//Treadmill(350.0 calories and 10.5 laps in 25.0 minutes)
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)
//Treadmill(350.0 calories and 10.5 laps in 25.0 minutes)
let runningWorkout = TreadmillWorkout(caloriesBurned: 350, minutes: 25, laps: 10.5)
在原来的Exercise中添加新的方法。
extension Exercise {
var caloriesBurnedPerMinute: Double {
return caloriesBurned / minutes
}
}
//11.166666666666666
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)
print(ellipticalWorkout.caloriesBurnedPerMinute)
//14.0
let runningWorkout = TreadmillWorkout(caloriesBurned: 350, minutes: 25, laps: 10.5)
print(runningWorkout.caloriesBurnedPerMinute)
用where子语句可以限制协议扩展的生效范围,比如Sequence就是一个系统的协议,用于实现集合迭代时的迭代对象的集合。如果要限制生效范围就可以用where语句来实现。
比如下例中,扩展Sequence协议,但只对元素类型为Exercise的序列有效。
extension Sequence where Iterator.Element == Exercise {
//添加新的方法
func totalCaloriesBurned() -> Double {
var total: Double = 0
for exercise in self { //self值为 Sequence
total += exercise.caloriesBurned
}
return total
}
}
//创建测试数组,输出 ~~ 685.0
let mondayWorkout: [Exercise] = [ellipticalWorkout, runningWorkout]
print(mondayWorkout.totalCaloriesBurned())
上述实现了给协议添加属性和方法功能,还可以利用协议扩展提供协议自身需求的默认实现。基实这段代码在之前出现了,为了突出功能,再详细解释下。通过这种实现那么所有符合Exercise协议的结构体默认就添加了此实现。
extension Exercise {
var description: String {
return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"
}
}
如果在符合协议的类型中定义了同名的方法,则上述的默认实现就会被覆盖。
//定义结构体TreadmillWorkout,并扩展Exercise协议
struct TreadmillWorkout: Exercise {
let name = "Treadmill Workout"
let caloriesBurned: Double
let minutes: Double
let laps: Double
}
//覆盖了 extension Exercise 的实现
extension TreadmillWorkout {
var description: String {
return "Treadmill(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"
}
}