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?
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.