计算属性和存储属性都长什么样子,一定要记忆深刻
存储属性
var name: String
var name = "a"
var property: Int = {
return 1
}()
计算属性
class sample {
var no1 = 0.0, no2 = 0.0
var length = 300.0, breadth = 150.0
var middle: (Double, Double) {
get{
return (length / 2, breadth / 2)
}
set(axis){ //注意这里的axis只是给newValue显示指定了参数名
no1 = axis.0 - (length / 2)
no2 = axis.1 - (breadth / 2)
}
}
}
或者
var computedValue: Int {
get { _backingValue }
set {
// 使用隐式 newValue 在计算属性的setter中,如果不显式指定参数名,则默认使用`newValue`作为参数名。但是,计算属性没有内置的旧值(oldValue)访问,因为计算属性本身不存储值
print("新值: \(newValue)")
_backingValue = newValue
}
}
在计算属性的setter中,如果不显式指定参数名,则默认使用`newValue`作为参数名。但是,计算属性没有内置的旧值(oldValue)访问,因为计算属性本身不存储值
那么如果我们需要一个旧的值呢?需要手动存储旧值:
private var _storage = 0
private var _oldValue = 0 // 额外存储旧值
var computedWithOldValue: Int {
get { _storage }
set {
_oldValue = _storage // 保存当前值为旧值
_storage = newValue // 更新为新值
print("旧值: \(_oldValue), 新值: \(newValue)")
}
}
只读的计算属性
var metaInfo: [String:String] {
return [
"head": self.head,
"duration":"\(self.duration)"
]
}
或者
var name: String {
get {
return ""
}
}
注意只读的计算属性并不是我们之前认识的readonly:因为只读计算属性在本类/结构体中也不能赋值
那么如何实现readonly呢?
private(set) var name: String
这个也很好理解,是有set方法是private的,所以在类/结构体外还是可以get的
struct test1{
private(set) var members :[String] = []
}
注意一个private(set)的集合,是不能添加和删除元素的
var t = test1()
t.members.append("a") //Cannot use mutating member on immutable value: 'members' setter is inaccessible
也可以结合计算属性使用
private var rawValue: Double = 0
private(set) var calibratedValue: Double {
get { rawValue * 1.25 }
set { rawValue = newValue / 1.25 }
}
关于计算属性的几个点
- 存储属性我们可以定义常量或者是变量,但是对于计算属性,必须定义为
变量
,并且计算属性在定义时必须包含类型
- 对于计算属性来说,
set
方法是可选的,而get
方法必不可少
-
let
的存储属性没有set
方法,只读的计算属性area
也没有set
方法;所以我们不能简单的通过有没有set方法来区分属性是计算属性还是存储属性
接下来继续看一下协议
- 协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。
- 协议不指定属性是存储属性还是计算属性,它只指定属性的名称和类型
- 在协议中,实例属性总是使用
var
声明为变量属性
- 在协议中,始终使用
static
关键字作为类属性声明的前缀, 在类中实现时,可以使用class
或static
关键字作类属性声明前缀(Class properties are only allowed within classes)
可读可写的属性在类型声明后通过写入{get set}表示可读属性通过写入{get}表示
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
如果协议要求属性是只读的,这意味着遵循协议的类型必须提供该属性,并且至少允许外部读取(get)。但是,在实现时,该属性可以是存储属性或计算属性,甚至可以是可读可写的(即可写的),因为协议只要求至少可读,不禁止可写。如下图,mustBeSettable至少要满足SomeProtocol中的“可读可写”
属性观察者属性观察器(didSet 和 willSet)是在属性的值被修改时触发的。这种修改必须通过显式的赋值语法完成
self.property = newValue
这种是观察不到的:
@State private var blurAmount = 0.0 {
didSet {
print("New value is \(blurAmount)")
}
}
Slider(value: $blurAmount, in: 0...20)
.onChange(of: blurAmount) { newValue in
print("New value is \(newValue)")
}
除了在声明语句中对属性赋值,其他对属性做赋值操作,必会触发观察器属性观察器(Property Observers)提供了两个特殊的关键字 newValue 和oldValue,用于在属性值变化时访问新值和旧值。它们分别用于 willSet 和 didSet 观察器中
var property: DataType = initialValue {
willSet {
// 使用 newValue 访问即将设置的值
// 当前属性值仍是旧值
}
didSet {
// 使用 oldValue 访问被覆盖的值
// 当前属性值已是新值
}
}
willSet中自带一个newValue的属性,oldValue用property自身即可访问,相同的didSet自带一个oldValue的属性,newValue用property自身即可访问