问题描述
当重写属性的didSet观察器导致递归时,为什么?
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
如果我用
更新QuadInt
class QuadInt : TwiceInt {
override var value:Int {
didSet {
super.value *= 4
}
}
}
q.value = 5 // q.value = 80
所以我想电话应该是这样的:
value = 5
QuadInt:didSet ( value *= 4 )
value = 20
TwiceInt:didSet ( value *= 2 )
value = 40
TwiceInt:didSet ( value *= 2 )
value = 80
这或多或少类似于在黑暗中拍摄.是否有任何文件说明属性更新时会发生什么?
您不能覆盖didSet
,这不是正常方法.实际上,您没有覆盖didSet
,而是覆盖了属性本身.
didSet
的工作方式类似于观察者,只是因为您在继承的属性上设置了自己的观察者,并不意味着任何其他观察者都会自动注销.因此,您的超类的观察者完全不受此und的影响,因此最后都将调用两个didSet
方法.
现在,如果您在自己的didSet
观察器中更改值,则不会导致递归,因为Swift运行时足够聪明,可以理解didSet
实现更改其自己观察到的属性不会被调用这样做之后再次.运行时知道当前正在执行的didSet
方法,并且如果变量在返回该方法之前发生更改,则不会再次执行该方法.这项检查似乎不适用于超类.
因此,*= 4
导致超类观察者被调用,这将设置*= 2
,这将导致再次调用子类观察者,这将再次设置*= 4
导致超类观察者被再次调用. ..依此类推.
通过显式使用super
,您将打破该循环,因为现在您没有设置覆盖的属性,而是继承的超级属性,并且您实际上并没有观察到该超级属性,而只是观察自己的覆盖的属性./p>
在某些语言中,使用覆盖方法会遇到类似的问题,典型的解决方案是在其中一个调用处显式使用super
.
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.
这篇关于Swift:覆盖didSet会导致递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!