停止触发 didSet 以避免在具有多个属性的 Swift 中使用 didSet 进行无限递归

问题描述 投票:0回答:2

我有一个具有两个属性的类,其值是使用 didSet 设置的。

class MyClass {
    var myProp1: Bool {
        didSet {
            self.myProp2 += blinks ? 1 : -1
        }
    }
    var myProp2: Int {
        didSet {
            self.myProp1 = (midi % 2) != 0
        }
    }
}

正如预期的那样,这将导致无限递归(myProp1 调用 myProp2 调用 myProp1 ...)。

是否可以在另一个 didSet 中调用属性时“关闭”didSet 行为?我知道 didSet 不会在 init() 中触发,这应该是相似的。

我知道 get/set 并且我知道创建方法的方法,但我问的是抑制导致无限递归的 didSet 行为。

swift recursion syntax infinite didset
2个回答
0
投票

无法禁用对

didSet
的调用。如果您有两个属性,其中设置一个应该更新另一个,则需要一个临时属性来告诉另一个
didSet
在通过第一个
didSet
间接调用时不要做任何事情。

这是一个使用私有

Bool
和一些额外检查的工作解决方案(我重命名了属性以匹配您在
didSet
块中的内容以制作一个工作示例):

class MyClass {
    var blinks: Bool {
        didSet {
            if !avoidLoop {
                avoidLoop = true
                self.midi += blinks ? 1 : -1
                avoidLoop = false
            }
        }
    }
    var midi: Int {
        didSet {
            if !avoidLoop {
                avoidLoop = true
                self.blinks = (midi % 2) != 0
                avoidLoop = false
            }
        }
    }

    // Added so the code can be tested
    init() {
        blinks = false
        midi = 0
    }

    private var avoidLoop = false
}

这里有一些示例代码,可以在无限递归的情况下毫无问题地运行:

var xxx = MyClass()
print(xxx.blinks, xxx.midi)
xxx.blinks = true
print(xxx.blinks, xxx.midi)
xxx.midi = 6
print(xxx.blinks, xxx.midi)

输出:

假 0
真 1
假 6


0
投票

给出的示例问题过于抽象和神秘,无法提供好的替代解决方案。也许解决您真正的潜在问题的最佳解决方案甚至不涉及财产观察员,但没有更多细节就无法知道。

防止这种递归的一种方法是将您的公共 API 从一些支持属性中分离出来。这里的公共计算属性只更新私有存储属性,但不会相互更新,所以没有递归。

class MyClass {
    let blinks = false
    let midi = 1
    
    public var myProp1: Bool {
        get { _myProp1 }
        set {
            self._myProp1 = myProp1
            self._myProp2 += blinks ? 1 : -1 // no recursion here
        }
    }
    public var myProp2: Int {
        get { _myProp2 }
        set {
            self._myProp1 = !midi.isMultiple(of: 2) // no recursion here
            self._myProp2 = myProp2
        }
    }
    
    private var _myProp1: Bool
    private var _myProp2: Int
}

我强烈怀疑这里最好的方法是将

myProp1
myProp2
拆分成一个新结构,并在每次突变时将其作为一个整体更新。

© www.soinside.com 2019 - 2024. All rights reserved.