didSet 中的循环
Loops in didSet
我们在 didSet 中使用循环时遇到了这种奇怪的行为。我们的想法是我们有一个具有树结构的数据类型,并且在每个元素中我们想要存储该项目所在的级别。因此,在 level
属性的 didSet 中,我们还将设置子级的级别属性。然而,我们意识到这仅在使用 forEach
时有效,而在使用 for .. in
时无效。这里有一个简短的例子:
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({ [=10=].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({ [=10=].printDepths() })
}
}
let item = Item(depth: 3)
item.depthA = 0
item.depthB = 0
item.printDepths()
当我 运行 时,我得到以下输出:
0 - 0
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
从for .. in
循环中调用时,它似乎不会调用subItems 属性的didSet。有谁知道为什么会这样吗?
更新:
问题不在于没有从 init 调用 didSet。我们之后更改属性(请参阅最后 4 行代码),两个深度属性中只有一个会将新值传播给子项
似乎在init
中没有调用didSet。
在 Swift REPL
上试过这一行
class A { var a: Int { didSet { print("A") } }; init() { a = 5 } }
然后调用A()
didSet 未被调用
但是
A().a = 7
发现调用了didSet
因此,解决方案是创建一个函数(最好是 final
),使您需要将效果放入 didSet
。然后从 didSet
和 init
调用它。你可以把它放在你的 class.
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()
...
如果您使用 defer 来更新任何可选属性或进一步更新您已经初始化的非可选属性,并且在您调用任何超级初始化方法之后,您的 willSet、didSet 等将被调用.
for item in subItems {
defer{
item.depthA = depthA + 1
}
}
当您使用 forEach 时,它会对元素生成 kindOf "contract" 并且因为它是一个不同于 for .. in[=21 的实例方法=]循环,会触发变量的didSet。上述情况适用于我们使用循环的地方,我们必须手动触发 didSet
这解决了我认为的问题。希望对你有帮助!!
我们在 didSet 中使用循环时遇到了这种奇怪的行为。我们的想法是我们有一个具有树结构的数据类型,并且在每个元素中我们想要存储该项目所在的级别。因此,在 level
属性的 didSet 中,我们还将设置子级的级别属性。然而,我们意识到这仅在使用 forEach
时有效,而在使用 for .. in
时无效。这里有一个简短的例子:
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({ [=10=].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({ [=10=].printDepths() })
}
}
let item = Item(depth: 3)
item.depthA = 0
item.depthB = 0
item.printDepths()
当我 运行 时,我得到以下输出:
0 - 0
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
从for .. in
循环中调用时,它似乎不会调用subItems 属性的didSet。有谁知道为什么会这样吗?
更新: 问题不在于没有从 init 调用 didSet。我们之后更改属性(请参阅最后 4 行代码),两个深度属性中只有一个会将新值传播给子项
似乎在init
中没有调用didSet。
在 Swift REPL
上试过这一行class A { var a: Int { didSet { print("A") } }; init() { a = 5 } }
然后调用A()
didSet 未被调用
但是
A().a = 7
发现调用了didSet
因此,解决方案是创建一个函数(最好是 final
),使您需要将效果放入 didSet
。然后从 didSet
和 init
调用它。你可以把它放在你的 class.
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()
...
如果您使用 defer 来更新任何可选属性或进一步更新您已经初始化的非可选属性,并且在您调用任何超级初始化方法之后,您的 willSet、didSet 等将被调用.
for item in subItems {
defer{
item.depthA = depthA + 1
}
}
当您使用 forEach 时,它会对元素生成 kindOf "contract" 并且因为它是一个不同于 for .. in[=21 的实例方法=]循环,会触发变量的didSet。上述情况适用于我们使用循环的地方,我们必须手动触发 didSet
这解决了我认为的问题。希望对你有帮助!!