swiftrecursionsyntaxinfinitedidset

Stop firing didSet to avoid infinite recursion with didSet in Swift with multiple properties


I have a class with two properties whose value is set using didSet.

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

This will result – as expected – in infinite recursion (myProp1 calling myProp2 calling myProp1...).

Is it possible to "turn off" the didSet behaviour when a property is called in another didSet? I know that didSet doesn't fire in init() and this should be similar.

I know about get/set and I know about the way to create a method, but I'm asking about suppressing didSet behaviour causing infinite recursion.


Solution

  • There's no way to disable a call to didSet. In a case where you have two properties where setting one should update the other, you need a temporary property to be able to tell the other didSet not to do anything when being called indirectly through the first didSet.

    Here's one working solution using a private Bool and some extra checks (I renamed the properties to match what you had in the didSet blocks to make a working example):

    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
    }
    

    Here's some sample code that runs without any problems with infinite recursion:

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

    Output:

    false 0
    true 1
    false 6