Swift:在递归中重写didSet结果

时间:2023-01-23 22:14:59

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

当重写属性的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

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?

这多少有点像在黑暗中拍摄。是否有关于属性更新时发生什么的文档?

4 个解决方案

#1


12  

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

你不能重写didSet,这不是一个普通的方法。实际上你没有重写didSet,而是重写了属性本身。

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.

didSet类似于观察者的工作,仅仅因为你在一个继承的属性上设置了自己的观察者,这并不意味着任何其他的观察者都是自动没有注册的。因此,超类的观察者完全不受这个und影响,因此两个didSet方法最终都将被调用。

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.

现在,如果您在自己的didSet observer中更改了一个值,那么这不会导致递归,因为Swift运行时足够聪明,可以理解didSet实现改变了自己的观察属性,在这样做之后不会再次调用。运行时知道当前正在执行的didSet方法,如果在此方法返回之前变量发生变化,则不会再次执行该方法。这张支票似乎并不适用于超类。

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.

因此,*= 4将调用超类观察者,该观察者集*= 2,这将使子类观察者再次被调用,这将再次设置*= 4,使超类观察者再次被调用……等等。

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.

通过显式地使用super,您可以打破这个循环,因为现在您不是在设置重写属性,而是在设置继承的super属性,而且您并没有真正地观察到这个super属性,您只是在观察自己重写的属性。

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.

您可能会遇到在某些语言中重写方法的类似问题,典型的解决方案是在其中一个调用中显式地使用super。

#2


3  

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.

在两个didSet块中都放置一个println(),您可以看到,它反复地先调用超级实现,然后是覆盖,然后是超级,然后是覆盖……直到它爆炸。

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

我只能想象这是Swift中的一个错误。我在Swift 1.2中也遇到了同样的问题(与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:

至少在我读到的时候,它肯定是有作用的。从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.

如果您在它自己的didSet observer中为属性赋值,那么您所赋的新值将替换刚才设置的值。

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.

在这两个检查中的第一个,didSet observer将currentLevel设置为不同的值。但是,这不会导致再次调用观察者。

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


2  

The problem is that you shouldn't use didSet to change the value because it will cause recursion, instead you should use set:

问题是,您不应该使用didSet来更改值,因为它会导致递归,而应该使用set:

var TwiceInt : Int {
    get {
        return TwiceInt
    }
    set (newValue) {
        TwiceInt *= 2
    }
}

#4


1  

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

出于某种原因,它仍然调用超类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.

在第一个例子中,你以递归结束因为设置四元组会触发超类didSet反过来会触发四元组确实设置等等。

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.

在第二个示例中,设置值会导致两个didSet同时发生,然后四元组didSet也会在上一次打开super didSet。

quad.value = 5

四。值= 5

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

value =*2(超类didSet) *4(子类didSet) *2(超类didSet) =80

#1


12  

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

你不能重写didSet,这不是一个普通的方法。实际上你没有重写didSet,而是重写了属性本身。

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.

didSet类似于观察者的工作,仅仅因为你在一个继承的属性上设置了自己的观察者,这并不意味着任何其他的观察者都是自动没有注册的。因此,超类的观察者完全不受这个und影响,因此两个didSet方法最终都将被调用。

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.

现在,如果您在自己的didSet observer中更改了一个值,那么这不会导致递归,因为Swift运行时足够聪明,可以理解didSet实现改变了自己的观察属性,在这样做之后不会再次调用。运行时知道当前正在执行的didSet方法,如果在此方法返回之前变量发生变化,则不会再次执行该方法。这张支票似乎并不适用于超类。

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.

因此,*= 4将调用超类观察者,该观察者集*= 2,这将使子类观察者再次被调用,这将再次设置*= 4,使超类观察者再次被调用……等等。

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.

通过显式地使用super,您可以打破这个循环,因为现在您不是在设置重写属性,而是在设置继承的super属性,而且您并没有真正地观察到这个super属性,您只是在观察自己重写的属性。

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.

您可能会遇到在某些语言中重写方法的类似问题,典型的解决方案是在其中一个调用中显式地使用super。

#2


3  

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.

在两个didSet块中都放置一个println(),您可以看到,它反复地先调用超级实现,然后是覆盖,然后是超级,然后是覆盖……直到它爆炸。

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

我只能想象这是Swift中的一个错误。我在Swift 1.2中也遇到了同样的问题(与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:

至少在我读到的时候,它肯定是有作用的。从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.

如果您在它自己的didSet observer中为属性赋值,那么您所赋的新值将替换刚才设置的值。

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.

在这两个检查中的第一个,didSet observer将currentLevel设置为不同的值。但是,这不会导致再次调用观察者。

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


2  

The problem is that you shouldn't use didSet to change the value because it will cause recursion, instead you should use set:

问题是,您不应该使用didSet来更改值,因为它会导致递归,而应该使用set:

var TwiceInt : Int {
    get {
        return TwiceInt
    }
    set (newValue) {
        TwiceInt *= 2
    }
}

#4


1  

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

出于某种原因,它仍然调用超类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.

在第一个例子中,你以递归结束因为设置四元组会触发超类didSet反过来会触发四元组确实设置等等。

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.

在第二个示例中,设置值会导致两个didSet同时发生,然后四元组didSet也会在上一次打开super didSet。

quad.value = 5

四。值= 5

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

value =*2(超类didSet) *4(子类didSet) *2(超类didSet) =80