Swift didSet中的循环
在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 {
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
}
}