具有双向绑定的KVO无限循环

时间:2017-06-28 18:17:41

标签: ios swift binding key-value-observing

我有两节课。班级A有班级B,但班级B不知道班级A的存在

这两个类都可以被外部因素(例如服务或逻辑)改变

但是我需要保持两个类与同一个值同步

由于班级A知道班级B,我可以在其值更改后进行直接作业

为了让课程B了解班级A,我决定实施KVO,这样,当班级A发生变化时会通知班级B

我的代码看起来像这样

class A : NSObject {
    var b : B

    @objc dynamic var anOtherString:String? {
        didSet{
            b.someString = self.anOtherString
        }
    }

    override init() {
        self.b = B()
        super.init()
        addObserver(self, forKeyPath: #keyPath(b.someString), options: [.old , .new], context: nil)
    }

    // MARK: - Key-Value Observing
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        if keyPath == #keyPath(b.someString) {
            // Update Time Label
            anOtherString = b.someString
        }
    }

}


class B : NSObject {
    @objc dynamic var someString: String?
}

问题是我的代码保持无限循环

因为每当我的班级B发生变化时,它都会通知班级A,而当班级A更新时,它会再次更改创建新通知的班级B的值等等...

我已经尝试分析Thread.callStackSymbols以检测周期并停止它但没有成功。

1 个答案:

答案 0 :(得分:0)

由于B因A而改变时不能允许A更新B,你可以有一个布尔变量来跟踪A是否需要更新B.

class A : NSObject {
    var b : B
    var bChanged: Bool = false // Logic to track if B made a change

    @objc dynamic var anOtherString:String? {
        didSet{
            // If B did not make a change, then update B
            if !bChanged {
                b.someString = self.anOtherString

            // Otherwise set the flag
            }else {
                bChanged = false
            }
        }
    }

    override init() {
        self.b = B()
        super.init()
        addObserver(self, forKeyPath: #keyPath(b.someString), options: [.old , .new], context: nil)
    }

    // MARK: - Key-Value Observing
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        if keyPath == #keyPath(b.someString) {
            // Update Time Label
            bChanged = true // B made a change
            anOtherString = b.someString
        }
    }
}