类型扩展
Swift提供一个叫扩展(extension)的特性,该特性就是为这种情况设计的。扩展能让你给已有的类型添加功能,可以用来扩展结构体、枚举和类,比如:
- 添加计算属性
- 添加新的初始化方法
- 添加协议实现
- 添加新方法
- 添加嵌入类型(枚举和结构体)
语法格式
extension TypeName {}
比如,为系统的Double对象添加一个mph方法,就可以用下列的代码实现
swift">extension Double {var mph: Double { return self }
}
扩展系统类库
swift">// 给Double起个别名
typealias Velocity = Double//添加两个计算属性
extension Velocity {var kph: Velocity { return self * 1.60934 }var mph: Velocity { return self }
}
扩展自定义类型
本例子中会以自定义的Car结构体为例进行说明,如何扩展自定义的类型的。
定义测试数据
定义测试结构体
- 自定义结构体
swift">// 自定义类型
struct Car {let make: Stringlet model: Stringlet year: Intlet color: Stringlet nickname: Stringvar gasLevel: Double { //可变的存储属性,附带了观察者实现willSet {precondition(newValue <= 1.0 && newValue >= 0, "New value must be between 0 and 1.")}}
}
定义测试协议
在自定义协议添加三个计算属性
swift">protocol Vehicle {var topSpeed: Velocity { get } var numberOfDoors: Int { get }var hasFlatbed: Bool { get }
}
扩展实现
用扩展方法实现协议
下面的代码其实就是协议实现的另一种写法
swift">// 添加Vehicle协议实现
extension Car: Vehicle {var topSpeed: Velocity { return 180 }var numberOfDoors: Int { return 4 }var hasFlatbed: Bool { return false }
}
增强自定义类型功能
- 添加初始化方法:前面提到过,如果结构体没有初始化方法的话,默认会自带一个成员初始化方法。如果想给结构体加一个新的初始化方法,同时又不想失去成员初始化方法,那就可以用扩展给结构体添加初始化方法。在扩展中为Car添加一个初始化方法,如下列代码清单所示。
Car的成员初始化方法之所以能保留下来,是因为新初始化方法是在扩展中定义和实现的,这个模式很有用。如果用自定义初始化方法的方式会覆盖掉默认的初始化方法
swift">//扩展初始化方法
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)
- 添加嵌套类型和扩展:Swift扩展还可以给已有的类型添加嵌套类型,比如下例给Car添加一个枚举来协助汽车的分类。
swift">//添加嵌入类型
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
- 添加函数方法:用扩展给已有类型添加新的函数,下例中就给Car类型添加了两个新的方法。
swift">//添加函数
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是一个系统协议
swift">protocol Exercise: CustomStringConvertible {var name: String { get }var caloriesBurned: Double { get }var minutes: Double { get }
}
扩展协议实现
扩展协议添加方法
在上面扩展的CustomStringConvertible协议中包含了一个名为description的函数,必须要实现,这段代码也可以写在上面的实现了,但为了演示协议的扩展,特意分出来一段代码实现,如下:
swift">extension Exercise {var description: String {return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"}
}
实现自定义的协议
自定义两个结构体,实现自定义的协议
swift">//定义结构体EllipticalWorkout,并扩展Exercise协议
struct EllipticalWorkout: Exercise {let name = "Elliptical Workout"let title = "Workout using the Go Fast Elliptical Trainer 3000"let caloriesBurned: Doublelet 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: Doublelet minutes: Doublelet 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中添加新的方法。
swift">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的序列有效。
swift">extension Sequence where Iterator.Element == Exercise {//添加新的方法func totalCaloriesBurned() -> Double { var total: Double = 0for exercise in self { //self值为 Sequencetotal += exercise.caloriesBurned}return total}
}//创建测试数组,输出 ~~ 685.0
let mondayWorkout: [Exercise] = [ellipticalWorkout, runningWorkout]
print(mondayWorkout.totalCaloriesBurned())
添加默认实现
上述实现了给协议添加属性和方法功能,还可以利用协议扩展提供协议自身需求的默认实现。基实这段代码在之前出现了,为了突出功能,再详细解释下。通过这种实现那么所有符合Exercise协议的结构体默认就添加了此实现。
swift">extension Exercise {var description: String {return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"}
}
如果在符合协议的类型中定义了同名的方法,则上述的默认实现就会被覆盖。
swift">//定义结构体TreadmillWorkout,并扩展Exercise协议
struct TreadmillWorkout: Exercise {let name = "Treadmill Workout"let caloriesBurned: Doublelet minutes: Doublelet laps: Double
}//覆盖了 extension Exercise 的实现
extension TreadmillWorkout {var description: String {return "Treadmill(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"}
}