Swift 中的属性观察者

属性观察者,用来监听和响应属性值的改变。在每次属性值被设置新值时都会被调用,即便设置的新值跟属性的当前值如出一辙。markdown

能够添加属性观察者的属性类型:ide

  • 定义的存储属性。
  • 继承的存储属性。
  • 继承的计算属性。

属性观察者经过下面的两个函数来监听:函数

  • willSet:在值存储前被调用。
  • didSet:在值存储后调用。

定义的存储属性

class Person {
    var name: String = "jack" {
        willSet(newName) {
            print("newName == \(newName)")
        }
        
        didSet {
            print("oldName == \(oldValue)")
        }
    }
}

let p = Person()
p.name = "rose" // name == jack;newName == rose name == rose;oldName == jack
复制代码

上面的代码定义了一个 Person 的类,该类包含一个 name 的存储属性。name 属性添加了属性观察者,并实现了 willSet/didSet 两个函数。spa

经过打印能够看出,在给 name 属性赋值以后,会先调用 willSet 打印 name == jack;newName == rose,再调用 didSet 打印 name == rose;oldName == jack。由此能够看出 willSet 是在值存储前被调用,而 didSet 是在值存储后调用。code

须要注意的是,当调用构造函数的时候,不会触发属性观察者。详见下面的代码:orm

class Person {
    var name: String {
        willSet(newName) {
            print("name == \(name);newName == \(newName)")
        }
        
        didSet {
            print("name == \(name);oldName == \(oldValue)")
        }
    }
    
    init(name: String) {
        self.name = name
    }
}

let p = Person(name: "rose")
复制代码

上面的代码中,将 name 属性的初始值删掉,并提供了一个构造函数。当调用 let p = Person(name: "rose") 这行代码时,并无任何打印。说明调用构造函数并不会触发属性观察者。继承

继承的存储属性

class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

class Student: Person {
    override var name: String {
        willSet(newName) {
            print("name == \(name);newName == \(newName)")
        }
        
        didSet {
            print("name == \(name);oldName == \(oldValue)")
        }
    }
}

let stu = Student(name: "jack")
stu.name = "rose" // name == jack;newName == rose name == rose;oldName == jack
复制代码

上面的代码声明了一个 Student 的类,继承自 Person。并对Student 继承 的 name 属性添加了属性观察者。get

经过打印能够看出,在给 name 属性赋值以后,会先调用 willSet 打印 name == jack;newName == rose,再调用 didSet 打印 name == rose;oldName == jack编译器

虽然,调用父类构造函数不会触发属性观察者,但若是在子类中修改属性值,是会触发属性观察者的。详见下面的代码:it

class Person {
    var name: String {
        willSet(newName) {
            print("name == \(name);newName == \(newName)")
        }
        
        didSet {
            print("name == \(name);oldName == \(oldValue)")
        }
    }
    
    init(name: String) {
        self.name = name
        self.name = "Person, \(name)"
    }
}

class Student: Person {
    override init(name: String) {
        super.init(name: name)
        self.name = "Student, \(name)"
    }
}

let p = Person(name: "mike") // 没有任何打印
let stu = Student(name: "robin") 
// name == Person, robin;newName == Student, robin
// name == Student, robin;oldName == Person, robin
复制代码

能够看到,在 Student 的构造器中添加 self.name = "Student, \(name)" 以后,就会触发属性观察者。

而在 Person 的构造器中,不管添加多少 self.name = "Person, \(name)" ,都不会触发属性观察者。

继承的计算属性

struct BodyInfo {
    var height = 0
    var weight = 0
}

class Person {
    var name: String
    var body = BodyInfo()
    
    var info: BodyInfo {
        get {
            return BodyInfo(height: body.height + 1, weight: body.weight + 1)
        }
        
        set {
            body.height = newValue.height + 1
            body.weight = newValue.weight + 1
        }
    }
    
    init(name: String) {
        self.name = name
    }
}

class Student: Person {
    override var info: BodyInfo {
        willSet(newInfo) {
            print("newInfo = \(newInfo)")
        }
        
        didSet {
            print("oldInfo = \(oldValue)")
        }
    }
}


let stu = Student(name: "jack")
stu.info = BodyInfo(height: 15, weight: 20)
// newInfo = BodyInfo(height: 15, weight: 20)
// oldInfo = BodyInfo(height: 1, weight: 1)
复制代码

须要说明上面的代码逻辑上狗屁不通,只是当作代码说明🤣。

上述代码中,Person 定义了一个 info 的计算属性,而后 Student 中继承了 info,并给它添加了属性观察者。

经过 stu.info = BodyInfo(height: 15, weight: 20) 的调用,能够看到输出的结果是符合预期的。

须要说明的是,不能再 Person 中给 info 添加属性观察者,由于 willSet/didSet 是不能和 get 同时出现的,感兴趣的同窗能够本身动手实践一下。编译器会报错:'willSet' cannot be provided together with a getter

总结

  • 能够添加属性观察者的属性类型: 定义的存储属性/继承的存储属性/继承的计算属性。
  • 属性观察者的两个函数:*willSet:在值存储前被调用;didSet:在值存储后调用。
  • 调用本类的构造函数不会触发属性观察者,但在子类中的构造器中修改属性会触发。
相关文章
相关标签/搜索