2

假设我有一个像这样的简单树状结构:

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

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

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

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

我尝试使用属性观察器来实现这一点,只要设置了它的祖先,就在节点的子节点上进行迭代,以便传播该值,但注意到既没有didSet也没有willSet被调用传递第一个孩子。这是我一直在使用的代码:

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
4

1 回答 1

3

我之前已经看到过这个问题,如果您尝试在同一类型的另一个实例上的 willSet / didSet 中设置属性,这似乎是一个 Swift 错误。

奇怪的是,语法很重要,而且似乎可以解决这个错误。例如,我可以通过将您的代码更改didSet为以下内容来使您的代码正常工作:

weak var ancestor: Tree? = nil {
    didSet {
        children.forEach { $0.ancestor = ancestor }
    }
}

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


更新

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

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

于 2016-12-02T21:00:46.823 回答