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

最后

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

相关推荐
HarderCoder19 小时前
Swift 中的不透明类型与装箱协议类型:概念、区别与实践
swift
HarderCoder19 小时前
Swift 泛型深度指南 ——从“交换两个值”到“通用容器”的代码复用之路
swift
东坡肘子20 小时前
惊险但幸运,两次!| 肘子的 Swift 周报 #0109
人工智能·swiftui·swift
胖虎120 小时前
Swift项目生成Framework流程以及与OC的区别
framework·swift·1024程序员节·swift framework
songgeb2 天前
What Auto Layout Doesn’t Allow
swift
YGGP2 天前
【Swift】LeetCode 240.搜索二维矩阵 II
swift
YGGP2 天前
【Swift】LeetCode 73. 矩阵置零
swift
非专业程序员Ping3 天前
HarfBuzz 实战:五大核心API 实例详解【附iOS/Swift实战示例】
android·ios·swift
Swift社区4 天前
LeetCode 409 - 最长回文串 | Swift 实战题解
算法·leetcode·swift
YGGP7 天前
【Swift】LeetCode 54. 螺旋矩阵
swift