如何以及何时使用 Swift 中的扩展
Swift 中的扩展允许你扩展类、结构体、协议和枚举,即使你无法访问源代码。
所谓的追溯建模允许你扩展你没有源代码 访问权限的类型。Swift 的这一强大功能使你能够在外部代码之上创建自定义层。作为一名应用程序开发者,何时以及如何使用它们是一项重要的技能,所以让我们深入了解一下。
什么是扩展?
从字面上讲,扩展扩展了现有代码。例如,你可能有一个带有名字和姓氏的 Person
结构体:
struct Person {
let firstName: String
let lastName: String
}
你可以为这个结构体创建一个扩展并添加一个 fullName
属性:
extension Person {
var fullName: String {
firstName + " " + lastName
}
}
在这种情况下,我们可以访问 Person
结构体的源代码,因此我们也可以决定在结构体本身内定义 fullName
计算属性:
struct Person {
let firstName: String
let lastName: String
var fullName: String {
firstName + " " + lastName
}
}
对于这个示例来说,这样做更有意义,因为它将所有可用属性保存在一个地方。
在没有源代码访问权限的情况下扩展类型
当由于缺少源代码访问权限而无法自定义类型的实现时,可以使用扩展来创建自定义访问器。例如,我们可以创建一个 Date
扩展来向当前值添加天数:
extension Date {
func addingDays(_ days: Int) -> Date {
Calendar.current.date(byAdding: .day, value: days, to: self)!
}
}
我们现在通过添加一个新方法扩展了 Date
类型。我们可以按如下方式使用该方法:
Date().addingDays(7)
即使我们无法访问 Date
类型的源代码,我们也能够使用自定义功能对其进行扩展。这可以是创建可发现代码的好方法,因为任何扩展方法都可以使用自动完成功能找到:
Swift 中的扩展允许你扩展类型,即使你没有源代码访问权限。
通过使你的自定义代码更易于发现,你可以让团队中的其他人发现自定义(通常有用的)扩展。这比创建每个人都应该知道的自定义类型(例如 DateDaysCalculator
)效果更好。
使用扩展创建默认协议实现
协议接口不允许你编写可选成员,即使某些方法对于每个实例可能不是必需的。其次,你可能希望提供默认行为并允许类型在需要时覆盖此行为。我们可以使用扩展来创建此行为。
例如,假设有一个 NameContaining
协议:
protocol NameContaining {
var firstName: String { get }
var lastName: String { get }
var fullName: String { get }
}
我们可以在没有任何更改的情况下使我们的 Person
结构体符合此协议:
struct Person: NameContaining {
let firstName: String
let lastName: String
var fullName: String {
firstName + " " + lastName
}
}
现在假设我们引入另一个 NameContaining
类型,但这次它是一个 Pet
:
struct Pet: NameContaining {
let firstName: String
let lastName: String
var fullName: String {
firstName + " " + lastName
}
}
你可以看到我们必须复制计算的 fullName
属性,即使它的功能与 Person
的实现相匹配。使用协议扩展,我们可以在保持相同功能的同时删除样板代码:
protocol NameContaining {
var firstName: String { get }
var lastName: String { get }
var fullName: String { get }
}
extension NameContaining {
/// 此计算属性为 `NameContaining` 协议提供默认实现。
var fullName: String {
firstName + " " + lastName
}
}
struct Person: NameContaining {
let firstName: String
let lastName: String
}
struct Pet: NameContaining {
let firstName: String
let lastName: String
}
此技术功能强大,可以防止你编写许多样板代码。如果你确实想提供自定义实现,你始终可以决定覆盖默认实现:
struct Person: NameContaining {
let firstName: String
let lastName: String
var fullName: String {
let firstNameFirstChar = firstName.first!.uppercased()
return firstNameFirstChar + ". " + lastName
}
}
struct Pet: NameContaining {
let firstName: String
let lastName: String
}
let person = Person(firstName: "Antoine", lastName: "van der Lee")
let pet = Pet(firstName: "Bernie", lastName: "van der Lee")
/// 使用 `fullName` 的自定义实现打印:“A. van der Lee”
print(person.fullName)
/// 使用 `fullName` 的默认实现打印:“Bernie van der Lee”
print(pet.fullName)
使用扩展 符合协议
Swift 中的扩展还允许你添加协议一致性。如果你想在源文件外部定义协议一致性,或者在无法访问源代码的情况下,这将非常有用。
例如,我们可以扩展 ContactsUI
框架的 CNContact
类型并使其符合我们的 NameContaining
协议:
import ContactsUI
extension CNContact: NameContaining {
var firstName: String {
givenName
}
var lastName: String {
middleName + familyName
}
var fullName: String {
givenName + middleName + familyName
}
}
这现在使我们能够创建与 NameContaining
协议一起使用的实例,而无需知道它们是处理 Pet
、Person
还是 CNContact
:
import SwiftUI
struct NameView: View {
let nameContaining: NameContaining
var body: some View {
Text("This person's name is: \(nameContaining.fullName)")
}
}
结论
Swift 中的扩展是一项强大的功能,允许你编写对结构体、类、协议或枚举的扩展。无论你是否可以访问源代码,扩展都可以扩展任何这些类型。将扩展与协议结合使用,你可以编写可重用代码,而无需太多样板。