Swift:覆盖didSet会导致递归 [英] Swift: Overriding didSet results in a recursion

查看:230
本文介绍了Swift:覆盖didSet会导致递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当重写属性的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会导致递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆