Swift get set willSet didSet 详解

1,885 阅读3分钟

属性分存储属性和计算属性。 存储属性:


var demo: Int = 0
let demo1: String = ""

计算属性:

我们先来写两个结构体:

struct Point {
	var x = 0.0
    var y = 0.0
}

struct Size {
	var width = 0.0
    var height = 0.0
}

这两个结构体分别表示,原点坐标和长宽值。有了它们,我们来定义一个矩形。

struct Rect {
	var orign = Point()
    var size = Size() 
    var center : Point {
    	get {
        	let center.x = orign.x + size.x/2
            let center.y = orign.y + size.y/2
            returen Point (x: center.x, y: center.y)
        }
        set {
        	center.x = newValue.x - size.x/2  
            center.y = newValue.y - size.y/2
            
        }
         // newValue 是系统定义的监测到新值标签名, 可以直接用。 你也可以写成自定义的:
         set(diyValue) {
        	 center.x = diyValue.x - size.x/2  
            center.y = diyValue.y - size.y/2
         } 
         
    } 
} 

可以看到, 在上面的结构体中,我们有两个存储属性 origin , size 分别存储原点坐标和长宽, 以及一个计算属性center 来得到这个矩形的中心坐标。

当我们实例化Rect 时,我们可以这样:

var square =  Rect(orign: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))

我们传进来了origin 的值和 size 的值, 由 存储属性 origin , size 进行存储。

如果这时我们打印 center 的值: print(square.center.x) 结果是: 5.0

也就是说,在你实例化Rect 后,通过Rect 结构体中的计算属性 center , 你得到了center 的值。 这时候你对center没有任何的修改, 你只是实例化了结构体,传值给存储属性,然后计算属性的值被计算出来。这就是get的作用。

如果你直接对center重新赋值: square.center = Point(x: 15, y: 15) print(square.orign.x)

结果是: 10.0

这就是set 的作用,监测到center的值被外部改变, 通过set 来对center 的值重新计算。

注: 如果get中是单一表达式,可以不加return。类似于:


		get {
            Point(x: orign.x + size.width/2, y: orign.y + size.height/2)
        }
        

willSet and didSet : 这两个被叫做属性观察器,官方定义如下: 属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。 你可以在以下位置添加属性观察器: 自定义的存储属性 继承的存储属性 继承的计算属性

我们先来写一个类, 用来计算走路的总步数:

class countSteps {
	var totalSteps: Int = 0 {
    	willSet(newSteps) {
        	print ("new steps is \(newSteps)" )
        }	
        
        didSet {
        	if newSteps > oldValue {     // oldValue是系统层面定义的 对旧值的标签名,不用提前声明,可以直接用。
            	print("increase \(totalStep - oldValue) steps")
            } else {
            	print("new totalStep is small than oldValue")
            }
        }
    } 
}

让我们来实例化这个类。 然后给totalSteps 赋值。


var counter = countSteps()

counter.totalSteps = 100

counter.totalSteps = 210

counter.totalSteps = 340

counter.totalSteps = 200

输出结果如下:


new steps is  100
increase 100 steps
new steps is  210
increase 110 steps
new steps is  340
increase 130 steps
new steps is  200
new totalStep is small than oldValue

也就是说, 当被属性观察器修饰的属性发生变化时,willSet会监测到新值的变化, 将新值传进willSet的结构体内进行下一步操作。它的作用不大。

而 didSet 的作用是,它会把旧值捕获到,当属性变化后,我们可以做点想要的操作。

除此之外,你也许还会在协议中碰到 get 与 set。

协议可以要求遵循它的类型提供特定名称和类型的实例属性或类型属性。 协议不指定属性是存储属性还是计算属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。 如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。 协议总是用 var 关键字来声明变量属性,在类型声明后加上 { set get } 来表示属性是可读可写的,可读属性则用 { get } 来表示

比如:

protocol FullyNamed {
    var fullName: String { get }
}

struct Person: FullyNamed {
    var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName 为 "John Appleseed"