2017-02-09 64 views
4

众所周知,当然,Set不会在didSet中再次运行同一个对象。 (example内在的保护奇怪地延伸到全班?

但是,看起来:该限制不仅适用于该对象,也可能适用于同一类别的任何对象

这里是复制粘贴测试用例的Playground。

class C { 
    var Test: Bool = false { 
     didSet { 
      print("test.") 
      for c in r { 
       c.Test = true 
      } 
     } 
    } 
    var r:[C] = [] 
} 
var a:C = C() 
var b:C = C() 
var c:C = C() 
a.r = [b, c] 
a.Test = false 

不行!

class C { 
    var Test2: Bool = false { 
     didSet { 
      print("test2.") 
      global.Test2 = true 
     } 
    } 
} 
var global:C = C() 
var a:C = C() 
a.Test2 = false 

不行!

  1. 这是一个Swift错误吗?

  2. 如果不是,什么是实际限制?它不会运行从didSet开始的任何didSet(任何)?同一个班级?同一个超类?要么?

  3. doco中的解释在哪里?

WTF。人们需要知道......具体的限制是什么?

+0

从理论上讲,其中一个孩子可能是自己的。意味着无限循环仍然可能发生。可能是一个错误,可能是一个功能它不是一个答案,因此它在评论中的原因:) – PeejWeej

+1

@JoeBlow:这个评论[Swift源代码](https://github.com/apple/swift/blob /master/lib/AST/Decl.cpp#L1072)可能是相关的:*“观察成员可以直接从它们的didSet/willSet指示符中进行访问,这样可以防止赋值成为无限循环。”*您也可以尝试在swift用户邮件列表,来自Swift团队的人员经常提供帮助。 –

+0

这看起来像一个相同的问题:[为什么没有无限循环在didSet?](http://stackoverflow.com/questions/29363170/why-no-infinite-loop-in-didset)。 –

回答

1

这是bug SR-419

从上错误的注释:

唉。我们真的需要检查属性访问的基础是静态的self

从我的实验中,似乎didSet观察者只有在任何对象上设置相同的属性时才会被调用。如果您设置了其他属性(即使在同一个对象上),也会正确调用观察者。

class A { 
    var name: String 
    var related: A? 
    var property1: Int = 0 { 
     didSet { 
      print("\(name), setting property 1: \(property1)") 

      self.property2 = 100 * property1 
      related?.property1 = 10 * property1 
      related?.property2 = 100 * property1 
     } 
    } 
    var property2: Int = 0 { 
     didSet { 
      print("\(name), setting property 2: \(property2)") 
     } 
    } 

    init(name: String) { 
     self.name = name 
    } 
} 

let a = A(name: "Base") 
a.related = A(name: "Related") 
a.property1 = 2 

输出:

基地设定属性1:2
基地设置属性2:200
相关,设置属性2:200

当预期输出应该是:

基地设定属性1:2
基地设置属性2:200
相关,设置属性1:20
相关,设置属性2:2000
相关,设置属性2:200

看来你还需要从观察者处直接指定该属性。一旦你进入另一个函数(或观察者),观察员开始工作:

var property1: Int = 0 { 
    didSet { 
     print("\(name), setting property 1: \(property1)") 

     onSet() 
    } 
} 

...  
func onSet() { 
    self.property2 = 100 * property1 
    related?.property1 = 10 * property1 
    related?.property2 = 100 * property1 
} 

这是最好的解决办法

另一个解决方法(感谢@Hamish)是嵌套任务包装成一个立即执行关闭:

var property1: Int = 0 { 
    didSet { 
     { 
      self.property2 = 100 * property1 
      related?.property1 = 10 * property1 
      related?.property2 = 100 * property1 
     }() 
    } 
} 

根据代码结束之前,你可能必须把它包装成括号或前一后插入一个分号声明。

+0

@JoeBlow从我的测试中,它可以正确处理无关的属性。另外,如果你从'property1.didSet'调用的'property2.didSet'中调用'property1.didSet',那么你会得到一个很好的无限循环。我没有测试过子类,但我认为它的工作原理是一样的 - 它仍然是同一个属性。 – Sulthan

+1

另一个有趣的解决方法 - 使用立即执行的闭包:'_ = {self.related?.property1 = 10 * self.property1}()'(由于某些原因,如果您不说' _ =')。 – Hamish

+0

@Hamish你将不得不添加一个手动返回使其返回void :) – Sulthan