计算属性 #
在 Swift 中,get
和 set
是用于定义计算属性的访问器。计算属性是一种特殊的属性,它没有直接存储值,而是通过一个方法来计算其值。
get
访问器:
- 用于获取计算属性的值。
- 当你访问计算属性时,
get
访问器会被调用。 get
访问器必须返回一个与属性类型匹配的值。
set
访问器:
- 用于设置计算属性的值。
- 当你给计算属性赋值时,
set
访问器会被调用。 set
访问器接收一个新值作为参数,并将其用于更新计算属性的内部状态。
示例:
class Rectangle {
var width: Double
var height: Double
var area: Double {
get {
return width * height
}
set {
width = sqrt(newValue)
height = sqrt(newValue)
}
}
init(width: Double, height: Double) {
self.width = width
self.height = height
}
}
let rectangle = Rectangle(width: 4.0, height: 3.0)
print(rectangle.area) // 输出:12.0
rectangle.area = 25.0 // 设置 area 为 25.0
print(rectangle.width) // 输出:5.0
print(rectangle.height) // 输出:5.0
在这个示例中,area
是一个计算属性,它没有直接存储值,而是通过 get
访问器来计算其值。当我们访问 rectangle.area
时,get
访问器会被调用,并返回 width * height
的结果。
set
访问器用于设置 area
的值。当我们给 rectangle.area
赋值时,set
访问器会被调用,并接收一个新值作为参数。在这个示例中,我们使用新值来更新 width
和 height
,以确保 area
的值始终等于 width * height
。
默认情况 #
- 你可以省略
get
访问器的关键字,因为它默认存在。 set
访问器必须有一个名为newValue
的参数,用于接收新值。- 计算属性不能直接存储值,它们的值始终是通过
get
访问器计算出来的。
你还可以为 set
访问器中的参数选择其他名称,但需要遵循以下步骤:
示例:
class Circle {
var radius: Double
var area: Double {
get {
return Double.pi * radius * radius
}
set(newAreaValue) { // 使用 newAreaValue 作为参数名
radius = sqrt(newAreaValue / Double.pi) // 使用 newAreaValue 访问参数值
}
}
init(radius: Double) {
self.radius = radius
}
}
let circle = Circle(radius: 2.0)
circle.area = 12.56 // 设置 area 为 12.56
print(circle.radius) // 输出:2.0
在这个例子中,我们使用 newAreaValue
作为 set
访问器中的参数名,并在访问器内部使用它来计算新的 radius
值。
注意:
- 虽然你可以使用其他参数名,但
newValue
是最常用的,因为它清晰地表明了这个参数代表的是要设置的新值。 - 如果你选择使用其他参数名,请确保它易于理解和维护。
- 如果你没有自定义参数名,Swift 会自动使用
newValue
作为参数名。 setter
只能有一个参数,如果内部需要使用多个参数进行计算,可以考虑使用元组或结构体来传递这些值。
属性观察器 #
在 Swift 中,属性观察器允许你在属性的值发生变化时执行特定代码,有助于在属性更新前后进行额外处理。Swift 提供了两种观察器:
1. willSet #
- 何时调用:在属性值即将被新值覆盖之前调用。
- 参数:默认接收一个名为 newValue 的新值,你也可以自定义参数名。
- 用途:适合在属性改变之前进行准备工作,比如验证、清理旧数据、发送通知等。
示例:
var score: Int = 0 {
willSet {
print("即将更新分数:当前分数 \(score),新分数 \(newValue)")
}
}
当你为 score 赋值时,会先调用 willSet。
2. didSet #
- 何时调用:在属性值已经被新值覆盖之后调用。
- 参数:默认接收一个 oldValue,即旧值,可以自定义参数名。
- 用途:适合在属性变化后进行后续处理,比如更新 UI、保存数据或触发其他逻辑。
示例:
var score: Int = 0 {
didSet {
print("分数已更新:旧分数 \(oldValue),当前分数 \(score)")
}
}
当 score 被赋予新值后,didSet 会被调用。
3. 注意事项 #
- 初始化时不触发:在属性初始化阶段,不会调用 willSet 和 didSet。
- 观察器不会影响属性本身:观察器只是附加的逻辑,不会改变属性的赋值过程。
- 计算属性 vs 存储属性:只有存储属性(包括从父类继承的属性)才可以使用属性观察器,而计算属性可以通过自定义 getter 和 setter 来实现类似功能。
4. 综合示例 #
下面是一个完整的例子,展示了如何同时使用 willSet 和 didSet:
struct Game {
var score: Int = 0 {
willSet(newScore) {
print("即将更新分数,从 \(score) 到 \(newScore)")
}
didSet {
print("分数更新完毕,从 \(oldValue) 到 \(score)")
}
}
}
var game = Game()
game.score = 100
输出:
即将更新分数,从 0 到 100
分数更新完毕,从 0 到 100
这种机制可以帮助你在数据变化前后插入自定义逻辑,是处理数据绑定、状态更新等场景的重要工具。