首页 Swift中的扩展
文章
取消

Swift中的扩展

Swift是一种现代化的编程语言,它拥有许多强大的特性。其中一个非常强大同时也很灵活的特性就是扩展。通过扩展,我们可以为已有的类型(包括类、结构体、枚举和协议)添加新的功能。

本文将深入介绍Swift中的扩展概念,包括扩展的语法、使用方法、限制,以及如何在项目中应用扩展。

什么是扩展?

扩展是一种增强现有类型的方法。在Swift中,我们可以通过扩展来:

  • 添加计算属性或实例方法
  • 定义下标脚本
  • 实现协议
  • 添加新的初始化器
  • 扩展已有的类型

等等。

较之继承和子类化,扩展具有更高的灵活性并且不需要修改原始类型。

如何声明一个扩展?

在Swift中,扩展使用extension关键字来声明。以下是一个简单的扩展示例:

1
2
3
4
5
6
7
8
9
extension String {
    func repeatNTimes(n: Int) -> String {
        var result = ""
        for _ in 0..<n {
            result += self
        }
        return result
    }
}

这个扩展给String类型添加了一个名为repeatNTimes的实例方法。该方法可以重复字符串n次并返回结果。

注意,在扩展中,我们可以访问原始类型的所有已有成员。例如,上面的repeatNTimes方法使用了String类型自带的+运算符。

扩展的使用

实例方法

除了上面提到的repeatNTimes方法,我们还可以在扩展中定义其他实例方法。例如:

1
2
3
4
5
6
7
8
9
extension Int {
    func isEven() -> Bool {
        return self % 2 == 0
    }

    func isOdd() -> Bool {
        return !self.isEven()
    }
}

这个扩展给Int类型添加了两个实例方法:isEvenisOdd。这两个方法判断整数是否为偶数和奇数。

在使用时,我们只需要像使用本来就属于Int类型的方法一样使用即可:

1
2
3
4
5
6
let num = 5
if num.isOdd() {
    print("\(num) is odd")
} else {
    print("\(num) is even")
}

计算属性

除了方法,我们还可以通过扩展来添加计算属性。例如:

1
2
3
4
5
6
7
extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}

这个扩展为Double类型添加了5个计算属性,分别表示公里、米、厘米、毫米和英尺之间的转换关系。

使用时,只需要像使用本来就属于Double类型的属性一样使用即可:

1
2
3
4
let lengthInMeters = 3.5
print("Length in meters: \(lengthInMeters)m")
print("Length in kilometers: \(lengthInMeters.km)km")
print("Length in centimeters: \(lengthInMeters.cm)cm")

下标脚本

扩展还可以添加下标脚本。下面是一个示例:

1
2
3
4
5
6
7
8
9
extension Array {
    subscript(indexSet: IndexSet) -> [Element] {
        var result = [Element]()
        for index in indexSet {
            result.append(self[index])
        }
        return result
    }
}

这个扩展为数组类型添加了一个下标脚本。该下标脚本接受一个IndexSet参数,并返回一个由对应元素组成的数组。

使用时,只需要像使用本来就属于Array类型的下标脚本一样使用即可:

1
2
3
4
let numbers = [1, 2, 3, 4, 5]
let indexSet = IndexSet([0, 2, 4])
let selectedNumbers = numbers[indexSet]
print(selectedNumbers) // Output: [1, 3, 5]

协议实现

除了添加成员之外,我们还可以通过扩展来为类型实现协议。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol Ageable {
    var age: Int { get set }
    mutating func celebrateBirthday()
}

struct Person: Ageable {
    var age = 0
    mutating func celebrateBirthday() {
        age += 1
    }
}

extension Double {
    var age: Int {
        return Int(self)
    }

    mutating func celebrateBirthday() {
        self += 1
    }
}

这个示例中,我们定义了一个名为Ageable的协议,并在Person结构体中实现了该协议。然后我们又在Double类型上添加了对Ageable协议的实现。

注意,在扩展中,我们只能通过计算属性和方法来实现协议中的要求。我们不能通过扩展添加存储属性或给已有的存储属性提供默认值。

初始化器

最后一个我们需要探讨的是扩展添加初始化器的能力。Swift中的扩展可以添加两种类型的初始化器:便捷初始化器和指定初始化器。

便捷初始化器

1
2
3
4
5
6
7
8
extension String {
    init(repeating pattern: String, count: Int) {
        self = ""
        for _ in 0..<count {
            self += pattern
        }
    }
}

这个示例中,我们通过扩展为String类型添加了一个便捷初始化器。该初始化器接受一个字符串参数和一个整数参数,用于将字符串重复指定次数并返回结果。

使用时,只需要像使用本来就属于String类型的初始化器一样使用即可:

1
2
let repeatedString = String(repeating: "Hello", count: 3)
print(repeatedString) // Output: "HelloHelloHello"

指定初始化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extension UIColor {
    convenience init?(hex: String) {
        guard hex.hasPrefix("#") else {
            return nil
        }

        let start = hex.index(hex.startIndex, offsetBy: 1)
        let hexColor = String(hex[start...])

        guard hexColor.count == 6 else {
            return nil
        }

        let scanner = Scanner(string: hexColor)
        var rgbValue: UInt64 = 0
        if scanner.scanHexInt64(&rgbValue) {
            let red = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
            let green = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
            let blue = CGFloat(rgbValue & 0x0000FF) / 255.0
            self.init(red: red, green: green, blue: blue, alpha: 1.0)
        } else {
            return nil
        }
    }
}

这个示例中,我们通过扩展为UIColor类型添加了一个指定初始化器。该初始化器接受一个字符串参数(代表十六进制颜色代码)并尝试将其转换为UIColor对象。

使用时,只需要像使用本来就属于UIColor类型的初始化器一样使用即可:

1
let color = UIColor(hex: "#FF0000")

扩展的限制

尽管扩展非常强大,但它们仍然有一些限制。具体而言,我们不能:

  • 重写已有的方法、属性或下标脚本
  • 添加存储属性或给已有的存储属性提供默认值
  • 提供需要类范围访问的计算属性、方法或下标脚本
  • 为协议添加存储属性

如何应用扩展?

扩展在Swift中非常常见,因此了解如何使用它们是至关重要的。以下是一些常见的用例:

对系统类型进行扩展

我们可以对Swift自带的类型(如Int、String、Array等)进行扩展,以便在日常编程中更轻松地使用它们。例如,我们可以为数组添加一个方法,该方法返回数组中的最大值:

1
2
3
4
5
extension Array where Element: Comparable {
    func max() -> Element? {
        return self.sorted().last
    }
}

这个扩展给数组类型添加了一个名为max的实例方法。该方法返回数组中的最大值。由于我们需要比较数组元素,因此我们使用了泛型和Comparable协议。

使用时,只需像使用本来就属于Array类型的方法一样使用即可:

1
2
let numbers = [3, 7, 1, 9, 4]
print(numbers.max()) // Output: Optional(9)

将相关的代码组织在一起

我们可以将相关的代码组织在一起,以便更好地管理和维护代码。例如,我们可以为一个项目中所有的UIViewController添加一个扩展,该扩展提供了一些通用的代码逻辑,如以下示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extension UIViewController {
    func showAlert(title: String, message: String) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(okAction)
        present(alertController, animated: true, completion: nil)
    }

    func showActivityIndicator() {
        let activityIndicator = UIActivityIndicatorView(style: .gray)
        activityIndicator.center = view.center
        view.addSubview(activityIndicator)
        activityIndicator.startAnimating()
    }

    func hideActivityIndicator() {
        for subview in view.subviews {
            if let activityIndicator = subview as? UIActivityIndicatorView {
                activityIndicator.stopAnimating()
                activityIndicator.removeFromSuperview()
            }
        }
    }
}

这个扩展为UIViewController类型添加了三个方法:showAlertshowActivityIndicatorhideActivityIndicator。这些方法在许多场景下都很有用,并且将通用代码从具体的视图控制器中分离出来。

遵循协议

我们可以通过扩展来遵循协议,从而将某些方法或属性封装在一起。例如,我们可以为UIViewController类型添加ErrorPresentable协议的实现,该协议定义了处理错误的方法:

1
2
3
4
5
6
7
8
9
10
11
12
protocol ErrorPresentable {
    func showError(_ error: Error)
}

extension UIViewController: ErrorPresentable {
    func showError(_ error: Error) {
        let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(okAction)
        present(alertController, animated: true, completion: nil)
    }
}

这个扩展为UIViewController类型添加了ErrorPresentable协议的实现。我们可以将其用于任何需要处理错误的场景,例如网络请求、数据解析等。

本文由作者按照 CC BY 4.0 进行授权

Swift中的嵌套类型

Swift中的协议