Inner didSet protection bizarrely extends to the whole class?

∥☆過路亽.° 提交于 2019-12-23 13:26:15

问题


It's well-known that, of course, didSet will not run on the same object again from inside a didSet. (example.)

However. It seems that: the restriction applies not only to that object, but to maybe any object of the same class.

Here are copy-paste test cases for Playground.

class C {
    var Test: Bool = false {
        didSet {
            print("test.")
            for c in r {
                c.Test = true
            }
        }
    }
    var r:[C] = []
}
var a:C = C()
var b:C = C()
var c:C = C()
a.r = [b, c]
a.Test = false

Does not work!

class C {
    var Test2: Bool = false {
        didSet {
            print("test2.")
            global.Test2 = true
        }
    }
}
var global:C = C()
var a:C = C()
a.Test2 = false

Does not work!

  1. Is this a Swift bug?

  2. If not, what is the actual restriction? It won't run ANY didSet (whatsoever) that starts from a didSet?; the same identical class?; the same super class?; or?

  3. Where exactly is this explained in the doco?

WTF. One needs to know ... what is the actual restriction specifically?


回答1:


This is bug SR-419.

From the comment on the bug:

Ugh. We really need to check that the base of the property access is statically self.

and from my experiments it seems that the didSet observer is not invoked only if you set the same property on any object. If you set any other property (even on the same object), the observer is invoked correctly.

class A {
    var name: String
    var related: A?
    var property1: Int = 0 {
        didSet {
            print("\(name), setting property 1: \(property1)")

            self.property2 = 100 * property1
            related?.property1 = 10 * property1
            related?.property2 = 100 * property1
        }
    }
    var property2: Int = 0 {
        didSet {
            print("\(name), setting property 2: \(property2)")
        }
    }

    init(name: String) {
        self.name = name
    }
}

let a = A(name: "Base")
a.related = A(name: "Related")
a.property1 = 2

Output:

Base, setting property 1: 2
Base, setting property 2: 200
Related, setting property 2: 200

when the expected output should be:

Base, setting property 1: 2
Base, setting property 2: 200
Related, setting property 1: 20
Related, setting property 2: 2000
Related, setting property 2: 200

It seems you also need to assign that property directly from the observer. Once you enter another function (or observer), the observers start working again:

var property1: Int = 0 {
    didSet {
        print("\(name), setting property 1: \(property1)")

        onSet()
    }
}

...    
func onSet() {
    self.property2 = 100 * property1
    related?.property1 = 10 * property1
    related?.property2 = 100 * property1
}

And that is the best workaround.

Another workaround (thanks @Hamish) is to wrap nested assignments into an immediately executed closure:

var property1: Int = 0 {
    didSet {
       {
           self.property2 = 100 * property1
           related?.property1 = 10 * property1
           related?.property2 = 100 * property1
       }()
    }
}

Depending on code before the closure, you might have to wrap it into parenthesis or insert a semicolon after the preceding statement.



来源:https://stackoverflow.com/questions/42144752/inner-didset-protection-bizarrely-extends-to-the-whole-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!