使用扩展可以给已经存在的类添加新的方法,尤其是你对某个类没有控制权的时候。扩展非常有用,尤其是帮你组织你的代码的时候。
什么是扩展
Swift的类、结构体、枚举或者协议都可以使用扩展来增加新的方法。即使这些类型不在你的控制范围内。如:
swift
class Airplane {
var altitude: Double = 0
func setAltitude(feet: Double) {
altitude = feet
}
}
有人想使用单位英尺来描述altitude
,有人想用单位米。如果这段代码你无法修改,你也不想继承这个类你就可以新建一个扩展。比如:
swift
extension Airplane {
func setAltitude(meter: Double) {
altitude = meter * 3.28084
}
}
这样就可以在一个Airplane
的对象上使用方法setAltitude
了。如:
swift
let boeing = Airplane()
boeing.setAltitude(meter: 12000)
print(boeing.altitude) // Output: 39370.08
扩展在编译的时候,扩展都会和原来的类整合到一起。
所以Swift的扩展可以:
- 添加方法和计算属性
- 提供新的初始化方法
- 使用
subscript()
定义下标 - 添加嵌套类型
- 实现某些协议,这很有用
- 使用协议扩展给协议增加默认实现
扩展虽然好用,但是无法改变一个类(结构体)的基本结构。只能用来增加功能,而不是替换。因此不能给一个类型增加新的属性,比如下面的例子这样是不行的:
swift
extension Airplane {
var speed: Int = 0
}
为什么不可以呢。因为给一个类型添加属性破坏了类型的结构。而且本来就可以通过继承的方式来更改类型的结构。
扩展的另外一个优点是,它可以帮助组织好你的代码等各种实际可操作性的特点。比如:
- 分离、组织代码
- 可以使用协议一致性
- 命名空间常量
- 添加方法、属性
- 扩展协议
我们一条一条来距离说明
分离、组织代码
使用扩展可以把一个类的代码分离,组织成不同的部分。比如在这通用类的部分只保留属性,在扩展里面添加方法。
比如:
swift
class Airplane
{
var speed: Double = 0
var altitude: Double = 0
var bearing: Double = 0
}
之后使用不同的扩展。首先:
swift
extension Airplane
{
func changeAltitude(altitude: Double) {
self.altitude = altitude
}
func changeBearing(degrees: Double) {
self.bearing = degrees
}
}
然后让飞机可以起飞和着陆:
swift
extension Airplane
{
func takeOff()
{
// Do take-off magic...
}
func land()
{
// Please stow hand luggage and move your seat to an upright position...
}
}
每个扩展可以有他们独立的文件,如果一个文件开始膨胀的时候,最好把他们按照功能放到不同的地方。比如,你新添加了一个view controller。它有两个主要功能,你可以把这两个功能放在两个扩展里。那个view controller可以继续当做一个完整的来使用。
使用协议一致性
你可以使用扩展让原来的类型遵守某一个协议。比如:
swift
class DetailViewController: UIViewController
{
// Do view controller magic...
}
extension DetailViewController: CLLocationManagerDelegate
{
// Put all location code in here...
}
在上面的代码里,DetailViewController
遵守了CLLocationManagerDelegate
协议,你可以放心的在这个扩展里添加代码了。
有一点要记住。扩展可不是你不合理的设计代码的接口。一个巨大的类分割成十个扩展还是一个巨大的类。合理的设计才是重中之重!
命名空间常量和嵌套类型
使用扩展可以添加静态常量和嵌套类型。
例如:
swift
extension Notification.Name {
static let statusUpdated = Notification.Name("status_updated")
}
以上的代码扩展了Notification.Name
,添加了statusUpdated
常量。可以这样使用:NotificationCenter.default.post(name: .statusUpdated, object: nil)
。
post(name:object:)
方法需要Notification.Name
的一个值作为第一个参数,这里就使用了刚刚添加的statusUpdated
。
添加嵌套类型:
swift
extension UserDefaults
{
struct Keys
{
static let useSync = "use_sync"
static let lastSync = "last_sync"
}
}
可以这样使用:
swift
UserDefaults.default.set(true, forKey: UserDefaults.Keys.useSync)
这样有两个好处:
Keys
嵌套类型只会在UserDefaults
类型里可以用。你不用担心会有什么类型的重名问题。这样的操作也很类似于其他语言里的类级别命名空间。- 在
UserDefaults
里使用的时候,如果直接输入use_sync
常量的话也不用担心系统提示会提示到什么其他地方定义的常量。
扩展计算属性和方法
这里举一个栗子。在Swift 4.2的时候数组没有shuffled()
方法。 继承数组可以添加这么一个方法,也可以使用全局方法处理数组。但是使用扩展可以直接给数组添加这个方法了。如:
swift
extension Array
{
func shuffled() {
// Shuffle array items and return shuffled array
}
}
这样shuffled()
方法就可以在任意的数组上使用了。
另一个很有帮助的例子:
swift
class Circle {
var radius: Double = 0
}
extension Circle
{
var circumference:Double {
return radius * .pi * 2
}
}
上面的代码首先定义了一个Circle
类,然后通过扩展给这个类添加了一个计算属性circumference
。可以这样使用这个属性:
swift
let circle = Circle()
circle.radius = 10
print(circle.circumference) // Output: 62.83185307179586
扩展协议
不同于协议一致(如上),协议扩展可以直接对一个协议进行扩展。可以在扩展协议的时候添协议的默认实现。
例如:
swift
protocol Edible {
func eat()
}
协议Edible
里定义了方法eat
,每个遵守这个协议的类型都要实现eat()
方法,例如:
swift
class Fish: Edible
{
func eat() {
print("**CHOMP** **CRUNCH** Eating the fish...")
}
}
下面来通过扩展Edible
协议给它增加一个默认实现:
swift
extension Edible
{
func eat() {
print("Eating the thing...")
}
}
这个扩展扩展的是Edible
,不是Fish
类。现在任何实现了协议Edible
的类型都可以使用eat
方法的默认实现了,例如:
swift
class Apple: Edible {
// Do nothing...
}
let apple = Apple()
apple.eat() // Output: Eating the thing...
在上例中就可以直接调用eat()
方法了。虽然Apple
类并没有提供eat
方法的实现。
最后
扩展增加了编写代码的方式,你可以给一个你无法修改的类增加方法、计算属性。你也可以使用扩展来更好的组织代码。用着着实不错!