Swift 的 属性 超级服务器不会被级联调用

Swift's property oveservers don't get called in cascade

假设我有一个像这样的简单 tree-like 结构:

class Tree {
    weak var ancestor: Tree?
    var children = [Tree]()
}

树的每个节点都保留对其 children 的引用,以及对 oldest 祖先的弱引用(以避免引用循环)。

我想在将子树的所有节点设置在其根节点时自动更新其所有节点的祖先 属性。

let grandParent = Tree()
let parent      = Tree()
let child       = Tree()
let grandChild  = Tree()

child.children.append(grandChild)
parent.children.append(child)
grandParent.children.append(parent)

// Should set ancestor in the whole hierarchy.
grandParent.ancestor = grandParent

我试图通过 属性 观察者来实现这一点,每当设置其祖先时迭代节点的 children 以便传播值,但都没有注意到 didSet也没有 willSet 被调用通过第一个 child。 这是我一直在使用的代码:

weak var ancestor: Tree? = nil {
    didSet {
        for child in self.children {
            child.ancestor = self.ancestor
        }
    }
}

正如预期的那样,grandParent 的祖先已正确设置,parent 的祖先也已正确设置。 然而,它的后代(childgrandChild)却不是。 请注意,我一直在用 willSet 观察到相同的结果(当然调整上面的代码以考虑到 self.ancestor 不会改变)。

有人可以指出我在这里遗漏了什么吗?


我可以用非常相似的方法用计算属性做我想做的事。 但我发现它不如有 属性 观察者的那个优雅,如果可能的话宁愿避免它。 尽管如此,这个片段还是把事情搞定了。

var ancestor: Tree? {
    get {
        return self._ancestor
    }

    set(newAncestor) {
        self._ancestor = newAncestor
        for child in self.children {
            child.ancestor = newAncestor
        }
    }
}

private weak var _ancestor: Tree? = nil

我之前看到过这个,它似乎是一个 Swift 错误,如果您尝试在另一个实例上的 willSet / didSet 中设置 属性 会触发该错误类型。

奇怪的是,语法很重要并且似乎可以解决该错误。例如,我可以通过将 didSet 更改为这样来使您的代码工作:

weak var ancestor: Tree? = nil {
    didSet {
        children.forEach { [=10=].ancestor = ancestor }
    }
}

它做的事情本质上是一样的,但不会触发编译器错误。


更新

这之前已在 swift.org 上作为错误归档并且仍然开放:https://bugs.swift.org/browse/SR-419

这里还有一些额外的评论阐明了原因以及可以避免的解决方法: https://twitter.com/uint_min/status/804795245728698368