Swift: Overriding didSet results in a recursion

核能气质少年 提交于 2019-12-04 10:36:53

问题


When overriding the didSet observer of a property results in recursion, why?

class TwiceInt {
    var value:Int  = 0 {
        didSet {
            value *= 2
        }
    }
}

class QuadInt : TwiceInt {
    override var value:Int {
        didSet {
            value *= 4
        }
    }
}

let t = TwiceInt()
t.value = 5 // this works fine


let q = QuadInt()
q.value = 5 // this ends up in recursion

If I update the QuadInt with

class QuadInt : TwiceInt {
    override var value:Int {
        didSet {
            super.value *= 4
        }
    }
}

q.value = 5 // q.value = 80

So I guess the call to be something like:

value = 5
QuadInt:didSet ( value *= 4 )
value = 20
TwiceInt:didSet ( value *= 2 )
value = 40
TwiceInt:didSet ( value *= 2 )
value = 80

This is more or less like shooting in the dark. Is there any document on what happens when a property updates?


回答1:


You cannot override didSet, it's not a normal method. Actually you didn't override didSet, you overrode the property itself.

didSet works like observers work and just because you set your own observer on a inherited property doesn't mean any other observer is automatically unregistered. So the observer of your superclass is entirely unaffected by this und thus both didSet methods will be called in the end.

Now if you change a value in your own didSet observer, this will not cause a recursion as the Swift runtime is smart enough to understand that a didSet implementation changing its own observed property doesn't expect to be called again after doing so. The runtime knows what didSet method it is currently executing and will not execute that method again if the variable changes before this method has returned. This check doesn't seem to work across superclasses.

So the *= 4 causes the super class observer to be called, which sets *= 2 and that causes the subclass observer to be called again, which will again set *= 4 causing the super class observer to be called again... and so on.

By explicitly using super, you break that cycle, as now you are not setting your overridden property, but the inherited super property and you are not really observing that super property, you are only observing your own overridden one.

You can run into a similar issue with overridden methods in some languages, where the typical solution is also to explicitly use super at one of the calls.




回答2:


Putting a println() in both didSet blocks, you can see that it repeatedly calls the super-implementation first, then the override, then super, then override... until it explodes.

I can only image that this is a bug in Swift. I get the same issue in Swift 1.2 (bundled with the Xcode 6.3 beta).


It should definitely function, at least as I read it. From https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254:

NOTE

If you assign a value to a property within its own didSet observer, the new value that you assign will replace the one that was just set.

and after their AudioChannel sample (quoted below the note):

NOTE

In the first of these two checks, the didSet observer sets currentLevel to a different value. This does not, however, cause the observer to be called again.

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // cap the new audio level to the threshold level
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // store this as the new overall maximum input level
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}



回答3:


it appears for some reason, despite the override it is still calling the superClass didSet.

In the first example you end up in recursion because setting quad sets off the superclass didSet which in turn sets off the quads did set etc ect.

In the second example setting the value causes both didSets to occur once each, then the quad didSet also sets of the super didSet on last time.

quad.value = 5

value =*2(superclass didSet) *4(subClass didSet) *2(superClass didSet) =80



来源:https://stackoverflow.com/questions/28067818/swift-overriding-didset-results-in-a-recursion

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