我有一个有两个属性的类,它们的值是使用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行为导致无限递归。
无法禁用对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)
输出:
false 0
true 1
false 6
给定的示例问题过于抽象和神秘,无法给出一个好的替代解决方案。也许你真正的潜在问题的最佳解决方案甚至不涉及属性观察者,但如果没有更多的细节,就无法知道。
防止这种递归的一种方法是将公共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
拆分为一个新的结构体,并在每次突变时将其作为一个整体更新。