Property observer, used to listen for and respond to changes in property values. This is called every time a property value is set to a new value, even if the new value is set to the property’s current value.

You can add the attribute type of the attribute observer:

  • Storage properties defined.
  • Inherited storage properties.
  • Inherited computed properties.

The attribute observer listens through two functions:

  • willSet: is called before value storage.
  • didSet: called after the value is stored.

Storage properties defined

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 == jackCopy the code

The above code defines a Person class that contains a storage property of name. The name attribute adds the attribute observer and implements both willSet/didSet functions.

As can be seen from printing, after assigning the name attribute, willSet is called to print name == jack; NewName == rose, didSet prints name == rose; OldName = = jack. This shows that willSet is called before the value is stored, while didSet is called after the value is stored.

Note that the attribute observer is not triggered when the constructor is called. See the following code:

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")Copy the code

In the above code, the initial value of the name attribute is removed and a constructor is provided. When the line let p = Person(name: “rose”) is called, nothing is printed. Description Calling the constructor does not trigger the property observer.

Inherited storage properties

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 = "rose" // name == jack; NewName == rose name == rose; oldName == jackCopy the code

The above code declares a Student class that inherits from Person. Add a property observer to the name property that Student inherits.

As can be seen from printing, after assigning the name attribute, willSet is called to print name == jack; NewName == rose, didSet prints name == rose; OldName = = jack.

Although calling the parent constructor does not trigger the property observer, it does if you modify the property value in a subclass. See the following code:

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 ") // No print let stu = Student(name: "robin") // name == Person, robin; NewName == Student, robin // name == Student, robin; oldName == Person, robinCopy the code

As you can see, adding self.name = “Student, \(name)” to the Student constructor triggers the attribute observer.

However, no amount of self.name = “Person, \(name)” added to the Person constructor will trigger the attribute observer.

Inherited computed properties

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)
Copy the code

Note that the above code is logical nonsense, just as the code description 🤣.

In the code above, Person defines a calculated attribute of info, which is then inherited from Student and added to it by attribute observer.

By calling stu.info = BodyInfo(height: 15, weight: 20), you can see that the output is as expected.

Note that you can’t add an observer to info in Person, because willSet/didSet can’t happen with GET. ‘willSet’ cannot be provided together with a getter.

conclusion

  • Attribute types that can be added to attribute observers: defined storage attribute/inherited storage attribute/inherited computed attribute.
  • Property observer of two functions: *willSet: called before the value is stored;didSet: called after the value is stored.
  • Calling the constructor of this class does not trigger the property observer, but modifying the property in a constructor in a subclass does.