Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift didSet中的循环_Swift_Foreach_For In Loop_Didset - Fatal编程技术网

Swift didSet中的循环

Swift didSet中的循环,swift,foreach,for-in-loop,didset,Swift,Foreach,For In Loop,Didset,在didSet中使用循环时,我们遇到了这种奇怪的行为。我们的想法是,我们有一个具有树结构的数据类型,在每个元素中,我们希望存储该项所在的级别。因此,在level属性的didSet中,我们还将设置子级的level属性。然而,我们意识到,这只在使用forEach时有效,而在使用for时无效。。在中。这里有一个简短的例子: class Item { var subItems: [Item] = [] var depthA: Int = 0 { didSet {

在didSet中使用循环时,我们遇到了这种奇怪的行为。我们的想法是,我们有一个具有树结构的数据类型,在每个元素中,我们希望存储该项所在的级别。因此,在
level
属性的didSet中,我们还将设置子级的level属性。然而,我们意识到,这只在使用
forEach
时有效,而在使用
for时无效。。在
中。这里有一个简短的例子:

class Item {

    var subItems: [Item] = []
    var depthA: Int = 0 {
        didSet {
            for item in subItems {
                item.depthA = depthA + 1
            }
        }
    }
    var depthB: Int = 0 {
        didSet {
            subItems.forEach({ $0.depthB = depthB + 1 })
        }
    }

   init(depth: Int) {
        self.depthA = 0

        if depth > 0 {
            for _ in 0 ..< 2 {
                subItems.append(Item(depth: depth - 1))
            }
        }
   }

   func printDepths() {
        print("\(depthA) - \(depthB)")

        subItems.forEach({ $0.printDepths() })
   }
}

let item = Item(depth: 3)
item.depthA = 0
item.depthB = 0
item.printDepths()
for.调用subItems属性时,它似乎不会调用该属性的didSet。。在
循环中。有人知道为什么会这样吗

更新:
问题不在于没有从init调用didSet。我们随后更改了属性(请参见代码的最后4行),两个深度属性中只有一个将新值传播给子级

init
中,似乎没有调用didSet

在Swift REPL上试过这句话

class A { var a: Int { didSet { print("A") } }; init() { a = 5 } }
然后调用
A()

didSet不被称为

但是

发现didSet被称为

因此,解决方案是制作一个函数(最好是
final
),使您的效果需要放入
didSet
。然后从
didSet
init
调用它。你可以把这个放在你的课堂上

final func updateA() {
    // Do your "didSet" here
}

var a: Int {
    didSet {
        updateA()
    }
}

init() {
    a = 5
    updateA()
}
因此,在你的情况下:

func updateDepthA() {
    for item in subItems {
        item.depthA = depthA + 1
    }
}

var depthA: Int = 0 {
    didSet {
        updateDepthA()
    }
}

...

init(depth: Int) {
    self.depthA = 0
    updateDepthA()

    ...

如果使用“延迟”来更新任何可选属性或进一步更新已初始化的非可选属性,并且在调用任何超级init方法之后,将调用willSet、didSet等

        for item in subItems {
            defer{
                item.depthA = depthA + 1
            }
        }
当您使用forEach时,它会与元素形成某种“契约”,因为它是一种不同于for的实例方法。。在循环中,它触发变量的didSet。上述情况适用于我们使用循环的情况,我们必须手动触发didSet


我认为这解决了问题。希望能有帮助

问题不在于没有从init调用didSet。我们在初始化后设置
depth
属性(参见最后四行代码),如果我们使用
进行初始化,则不会调用子属性的didSet。。在
循环中。谢谢!但是为什么我需要在
中为。。在
循环和
forEach
中,使用forEach时没有必要?@M.Schrick它与元素有某种“契约”,因为它是一种与for不同的实例方法。。在循环中,它触发变量的didSet。上述情况适用于我们使用循环的情况,我们必须手动触发didSet。我希望它是有意义的?你手头有没有一个链接,我可以在那里读到它?@M.Schrick,因为它是一个高阶函数(forEach)有关更多信息,您可以研究闭包作为参数。我想了解所有这些高阶函数如何工作的基本知识对我来说很清楚。我想说,这种行为非常复杂,足以保证函数的正确性。首先,它消除了关于是否在
init
中调用它的任何疑问。使用闭包(正如其他人所指出的,
defer
)可以逃避(当前已中断的)语义分析,从而防止从内部调用
didSet
观察者<子项{{item.depthA=depthA+1}()}
中的项的代码>也有效。和:)
func updateDepthA() {
    for item in subItems {
        item.depthA = depthA + 1
    }
}

var depthA: Int = 0 {
    didSet {
        updateDepthA()
    }
}

...

init(depth: Int) {
    self.depthA = 0
    updateDepthA()

    ...
        for item in subItems {
            defer{
                item.depthA = depthA + 1
            }
        }