簡體   English   中英

通過函數設置內部時,didSet如何再次調用?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM