Swift — 属性(Properties)

计算属性 #

官方文档

在 Swift 中,getset 是用于定义计算属性的访问器。计算属性是一种特殊的属性,它没有直接存储值,而是通过一个方法来计算其值。

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 访问器会被调用,并接收一个新值作为参数。在这个示例中,我们使用新值来更新 widthheight,以确保 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

这种机制可以帮助你在数据变化前后插入自定义逻辑,是处理数据绑定、状态更新等场景的重要工具。

本文共 1509 字,创建于 Feb 18, 2025
相关标签: Xcode, SwiftUI