首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift:覆盖didSet会导致递归

Swift:覆盖didSet会导致递归
EN

Stack Overflow用户
提问于 2015-01-21 20:57:12
回答 3查看 7.8K关注 0票数 13

当重写属性的didSet观察者导致递归时,为什么?

代码语言:javascript
复制
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

代码语言:javascript
复制
class QuadInt : TwiceInt {
    override var value:Int {
        didSet {
            super.value *= 4
        }
    }
}

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

所以我猜调用应该是这样的:

代码语言:javascript
复制
value = 5
QuadInt:didSet ( value *= 4 )
value = 20
TwiceInt:didSet ( value *= 2 )
value = 40
TwiceInt:didSet ( value *= 2 )
value = 80

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

EN

回答 3

Stack Overflow用户

发布于 2016-07-06 21:08:21

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

didSet的工作方式就像观察者一样,仅仅因为您在继承的属性上设置了自己的观察者并不意味着任何其他观察者都会自动取消注册。所以你的超类的观察者完全不受这个函数的影响,因此这两个didSet方法最终都会被调用。

现在,如果您更改自己的didSet观察器中的值,这将不会导致递归,因为Swift运行时足够智能,能够理解更改自己的观察属性的didSet实现不会在更改后再次被调用。运行库知道它当前正在执行的didSet方法,如果变量在此方法返回之前发生更改,则不会再次执行该方法。这个检查似乎不能跨超类工作。

因此,*= 4会调用超类观察器,这会设置*= 2,这会再次调用子类观察器,这会再次设置*= 4,从而再次调用超类观察器...诸若此类。

通过显式地使用super,您打破了这个循环,因为现在您设置的不是覆盖属性,而是继承的超级属性,并且您实际上并没有观察到该超级属性,而只是观察到了您自己的被覆盖的属性。

在某些语言中使用被覆盖的方法也会遇到类似的问题,典型的解决方案也是在其中一个调用中显式使用super

票数 19
EN

Stack Overflow用户

发布于 2015-03-03 05:05:33

在两个didSet块中都放入一个println(),您可以看到它首先重复调用超级实现,然后是override,然后是Super...直到它爆炸。

我只能想象这是Swift中的一个bug。我在Swift 1.2 (与Xcode 6.3测试版捆绑在一起)中遇到了同样的问题。

它肯定会起作用,至少在我读到它的时候是这样。来自https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254

笔记

如果在属性自己的didSet观察器中为该属性赋值,则所赋值的新值将替换刚刚设置的值。

在他们的AudioChannel示例之后(在注释下面引用):

笔记

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

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2015-03-03 05:35:19

出于某种原因,它出现了,尽管它仍然调用superClass didSet。

在第一个例子中,你会在递归中结束,因为设置quad会触发超类didSet,而超类又会触发quad、did、set等。

在第二个示例中,设置该值会导致两个didSets每次都出现一次,然后四元didSet还会设置上一次的超级didSet。

quad.value =5

值=*2(超类didSet) *4(subClass didSet) *2(superClass didSet) =80

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28067818

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档