在 Swift 中,extension
是一种非常有用的语言特性,它可以为一个已有的类型(包括类、结构体和枚举)添加新的属性、方法、下标和协议等功能,而无需修改原始类型的定义。使用 extension
可以让我们更加方便地扩展和修改现有的代码,同时也可以使代码更加清晰和易读。
下面是一些 extension
的使用示例:
1. 为类型添加新的方法
extension Int {
func times(_ closure: () -> Void) {
for _ in 0..<self {
closure()
}
}
}
3.times { print("Hello") } // 输出三次 "Hello"
在上面的代码中,我们为 Int
类型添加了一个名为 times
的方法,这个方法接受一个闭包作为参数,并在闭包中执行指定次数的操作。在调用时,我们可以使用 3.times { print("Hello") }
的语法来输出三次 "Hello"。
2. 为类型添加新的计算属性
extension Double {
var squared: Double {
return self * self
}
}
let x = 3.0
let y = x.squared // y 的值为 9.0
在上面的代码中,我们为 Double
类型添加了一个名为 squared
的计算属性,这个属性返回当前值的平方。在调用时,我们可以使用 x.squared
的语法来计算 x
的平方并将结果赋值给 y
。
3. 为类型添加新的下标
extension Array {
subscript(safe index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
let array = [1, 2, 3]
let element = array[safe: 10] // element 的值为 nil
在上面的代码中,我们为 Array
类型添加了一个名为 safe
的下标,这个下标接受一个整数作为参数,并返回数组中对应索引位置的元素(如果该索引位置存在的话),否则返回 nil
。在调用时,我们可以使用 array[safe: 10]
的语法来获取数组中索引为 10 的元素,由于该索引位置不存在,因此返回值为 nil
。
4. 为类型遵循新的协议
protocol Runnable {
func run()
}
extension UIViewController: Runnable {
func run() {
print("ViewController is running")
}
}
let viewController = UIViewController()
viewController.run() // 输出 "ViewController is running"
在上面的代码中,我们为 UIViewController
类型遵循了一个名为 Runnable
的协议,并为该类型添加了一个 run
方法。在调用时,我们可以使用 viewController.run()
的语法来执行 ViewController
中的 run
方法,并输出 "ViewController is running"。
总之,extension
是一种非常有用的语言特性,可以让我们更加灵活地扩展和修改现有的代码,从而提高开发效率和代码质量。
问题:你能通过extension(扩展)保存一个属性吗?请解释一下原因。
在 Swift 中,我们可以使用 extension
来为一个已有的类型添加新的属性和方法。但是,我们不能使用 extension
来添加存储属性,因为存储属性必须在类型的定义中进行声明。
因为 extension
是在类型的定义之外添加新的功能,它只能添加计算属性,也就是只有 getter 和 setter 方法的属性,而没有实际的存储空间来存储属性值。因此,如果我们在 extension
中尝试添加一个存储属性,编译器会提示错误。
下面是一个示例代码:
struct Person {
var name: String
}
extension Person {
var age: Int // 编译错误:Extensions must not contain stored properties
}
在上面的代码中,我们尝试在 Person
结构体的 extension
中添加一个 age
属性,但是编译器会提示错误,因为我们不能在 extension
中添加存储属性。
不过,我们可以在 extension
中添加计算属性,这些属性会在每次访问时计算出一个值,如下所示:
extension Person {
var fullName: String {
return "\(name)"
}
}
在上面的代码中,我们在 Person
结构体的 extension
中添加了一个 fullName
计算属性,这个属性只有 getter 方法,每次访问时都会计算出一个完整的姓名并返回。