据我所知和这个线程中提到的,如果我在其didSet观察者中设置属性值,它不应该再次触发观察者.好的,然后我写了一段这样的代码:
class B {
var i = 0 {
didSet {
print("didSet called")
self.i += 1
}
}
}
var y = B()
y.i = 2
print(y.i)
Run Code Online (Sandbox Code Playgroud)
此代码按预期打印"didSet called"和3输出.但我对此代码做了一些小改动,如下所示:
class B {
var i = 0 {
didSet {
print("didSet called")
doit(val: self)
}
}
func doit(val: B) {
val.i += 1
}
}
var y = B()
y.i = 2
print(y.i)
Run Code Online (Sandbox Code Playgroud)
但现在它陷入无限循环打印"didSet called".为什么如果我通过函数参数传递给内部didSet变量的值,它会didSet再次触发吗?由于传递的对象应该引用同一个对象,我不知道为什么会发生这种情况.我测试过,如果我通过闭包didSet而不是普通函数设置它,它会再次进入无限循环.
更新: 有趣的是,即使这会触发无限循环:
class B {
var i = 0 {
didSet {
print("called")
doit()
}
}
func doit() {
self.i += 1
}
}
Run Code Online (Sandbox Code Playgroud)
在与 Swift github 核实并询问有关此问题的问题后,我发现这个问题看起来更复杂。但是关于这个问题有一个特定的规则:
didSet仅当didSet可以通过直接内存访问来访问其自己的观察者内的属性时,观察者才会触发。
问题是当直接访问属性时会有点模棱两可(除非你是 Swift 的开发者)。对我的问题有影响的一个重要特征是:
类实例方法从不直接访问类属性。
这句话显示了我的代码存在问题,尽管我可以争辩说,当您在didSetobserve 中调用实例成员时,它应该能够直接访问属性。当我有这样的代码时:
class B {
var i = 0 {
didSet {
print("called")
doit()
}
}
func doit() {
self.i += 1
}
}
Run Code Online (Sandbox Code Playgroud)
doit()函数无法i直接访问,didSet再次触发导致无限循环。
现在有什么解决方法?
您可以inout用于将属性从其自身传递didSet到实例函数而不触发didSet. 像这样的东西:
class B {
var i = 0 {
didSet {
print("called")
doit(&i)
}
}
func doit(_ i: inout Int) {
i += 1
}
}
Run Code Online (Sandbox Code Playgroud)
还有最后一件事。从Swift 5开始,为自己的属性选择直接内存访问的条件didSet将变得更加受限。基于github,只有使用直接内存访问的条件如下:
Run Code Online (Sandbox Code Playgroud)Within a variable's own didSet/willSet specifier, access its storage directly if either: 1) It's a 'plain variable' (i.e a variable that's not a member). 2) It's an access to the member on the implicit 'self' declaration. If it's a member access on some other base, we want to call the setter as we might be accessing the member on a *different* instance.
这意味着像下面这样的代码会触发无限循环,而现在不会:
class B {
var i = 0 {
didSet {
print("called")
var s = self
s.i += 1
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
465 次 |
| 最近记录: |