Swfit扩展解析

使用扩展可以给已经存在的类添加新的方法,尤其是你对某个类没有控制权的时候。扩展非常有用,尤其是帮你组织你的代码的时候。

什么是扩展

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方法的实现。

最后

扩展增加了编写代码的方式,你可以给一个你无法修改的类增加方法、计算属性。你也可以使用扩展来更好的组织代码。用着着实不错!

相关推荐
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
KeithTsui2 天前
ZFC in LEAN 之 前集的等价关系(Equivalence on Pre-set)详解
开发语言·其他·算法·binder·swift
袁代码2 天前
Swift 开发教程系列 - 第4章:函数与闭包
ios·swift·ios开发
安泽13143 天前
高德地图美食
开发语言·swift·美食
袁代码3 天前
Swift 开发教程系列 - 第2章:Swift 基础语法
swift·ios开发·基础教程
袁代码3 天前
Swift 开发教程系列 - 第1章:Swift 简介与开发环境配置
swift·ios开发·基础教程
孚亭4 天前
一些swift问题
swift
莫问alicia4 天前
echarts 实现3D饼状图 加 label标签显示
前端·3d·echarts·swift
uiop_uiop_uiop6 天前
iOS Swift5算法恢复——HMAC
ios·iphone·swift
東三城7 天前
【ios】---SwiftUI开发从入门到放弃
ios·swiftui·swift·1024程序员节