2017-02-09 66 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