[英]How is didSet called again when setting inside through a function?
據我所知和這個線程中提到的,如果我在didSet
觀察器中設置一個屬性值,它不應該再次觸發觀察者。 好的,然后我寫了一段這樣的代碼:
class B {
var i = 0 {
didSet {
print("didSet called")
self.i += 1
}
}
}
var y = B()
y.i = 2
print(y.i)
此代碼按預期打印"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)
但現在它陷入無限循環打印"didSet called"
。 為什么我通過傳遞函數參數將值設置為didSet
內的變量,它didSet
再次觸發didSet
? 由於傳遞的對象應該引用同一個對象,我不知道為什么會發生這種情況。 我測試過,如果我通過didSet
的閉包而不是普通函數設置它,它會再次進入無限循環。
更新:有趣的是,即使這會觸發無限循環:
class B {
var i = 0 {
didSet {
print("called")
doit()
}
}
func doit() {
self.i += 1
}
}
在使用Swift github檢查並詢問有關此問題的問題之后,我發現這個問題看起來更復雜。 但是有一個關於這個問題的具體規則:
didSet
觀察者只有在自己的didSet
觀察者中訪問屬性才能通過直接內存訪問才能觸發。
問題是, 當訪問屬性是直接的時 ,它有點模棱兩可(除非您是Swift的開發人員)。 對我的問題有影響的一個重要特征是:
類實例方法永遠不會直接訪問類屬性。
這句話顯示了我的代碼的問題,盡管我可以爭辯說當一個實例成員應該能夠在didSet
observe中調用它時直接訪問屬性。 當我有這樣的代碼:
class B {
var i = 0 {
didSet {
print("called")
doit()
}
}
func doit() {
self.i += 1
}
}
doit()
函數無法直接訪問i
,觸發didSet
再次導致無限循環。
現在的解決方法是什么?
您可以使用inout
將屬性從其自己的didSet
到實例函數,而不觸發didSet
。 像這樣的東西:
class B {
var i = 0 {
didSet {
print("called")
doit(&i)
}
}
func doit(_ i: inout Int) {
i += 1
}
}
最后一件事。 啟動Swift 5 ,為其自己的didSet
屬性選擇直接內存訪問的didSet
將變得更加受限制。 基於github,只有使用直接內存訪問的條件如下:
Within a variable's own didSet/willSet specifier, access its storage directly if either: 1) It's a 'plain variable' (ie 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
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.