Swift: how to change a property's value without calling its didSet function

后端 未结 4 405
谎友^
谎友^ 2021-01-01 12:41

How can you set a property\'s value in Swift, without calling its didSet() function outside of an initialization context? The code below was a failed experimen

相关标签:
4条回答
  • 2021-01-01 12:51

    A possible hack around this is to provide a setter which bypasses your didSet

     var dontTriggerObservers:Bool = false
        var selectedIndexPath:NSIndexPath? {
            didSet {
                if(dontTriggerObservers == false){
                    //blah blah things to do
                }
            }
        }
        var primitiveSetSelectedIndexPath:NSIndexPath? {
            didSet(indexPath) {
                dontTriggerObservers = true
                selectedIndexPath = indexPath
                dontTriggerObservers = false
            }
        }
    

    Ugly but workable

    0 讨论(0)
  • 2021-01-01 13:08

    If you exactly know when you want to apply side effects just make it explicitly:

    1 Solution:

    func noside(newValue : Int) {
        hoodwink = newValue
    }
    
    func withside(newValue : Int) {
        self.hoodwink = newValue
        toggle = !toggle
    }
    

    2 Solution:

    var toggle : Bool = false
    var hoodwink : Int = 0 
    var hoodwinkToggle: Int {
        get { return hoodwink }
        set(newValue) {
            hoodwink = newValue
            toggle = !toggle
        }
    }
    
    1. func setHoodwinkWithToggle(hoodwink: Int) {...}
    2. ....

    I think these solutions will be more clear and readable, then using one variable which at some calls should have side effects and shouldn't at others.

    0 讨论(0)
  • 2021-01-01 13:11

    The issue I was trying to solve is I wanted to get a doCommonUpdate call when one value changed, or one if both changed at the same time. Doing this created a recursion because if either value changed it would call didSet each time. Example setup, mine was more involved:

    class Person {
        var first: String = "" {
            didSet {
                updateValues(first: first, last: last)
            }
        }
        var last: String = "" {
            didSet {
                updateValues(first: first, last: last)
            }
        }
        init() {}
        init(first: String, last: String) {
            self.first = first
            self.last = last
        }
        // also want to set both at the same time.
        func updateValues(first: String, last: String) {
    //        self.first = first // causes recursion.
    //        self.last = last
            doCommonSetup(first: first, last: last)
        }
        func doCommonSetup(first: String, last: String) {
            print(first, last)
        }
    }
    let l = Person()
    l.first = "Betty"
    l.last = "Rubble"
    _ = Person(first: "Wilma", last: "Flintstone")
    
    > Betty 
    > Betty Rubble
    

    Note Wilma does not print because of the commented out lines.

    My solution was to move all those variables into a separate struct. The this approach solves the problem and has the side benefit of creating a another grouping which helps meaning. We still get the doCommonSetup when either value changes independently and when we get both values changed at the same time.

    class Person2 {
        struct Name {
            let first: String
            let last: String
        }
        var name: Name
        init() {
            name = Name(first: "", last: "")
        }
        init(first: String, last: String) {
            name = Name(first: first, last: last)
            updateValues(name: name)
        }
        var first: String = "" {
            didSet {
                name = Name(first: first, last: self.last)
                updateValues(name: name)
            }
        }
        var last: String = "" {
            didSet {
                name = Name(first: self.first, last: last)
                updateValues(name: name)
            }
        }
        // also want to set both at the same time.
        func updateValues(name: Name) {
            self.name = name
            doCommonSetup(name: name)
        }
        func doCommonSetup(name: Name) {
            print(name.first, name.last)
        }
    }
    
    let p = Person2()
    p.first = "Barney"
    p.last = "Rubble"
    _ = Person2(first: "Fred", last: "Flintstone")
    
    > Barney 
    > Barney Rubble
    > Fred Flintstone
    
    0 讨论(0)
  • 2021-01-01 13:12

    What you do in Objective-C to "avoid side effects" is accessing the backing store of the property - its instance variable, which is prefixed with underscore by default (you can change this using the @synthesize directive).

    However, it looks like Swift language designers took specific care to make it impossible to access the backing variables for properties: according to the book,

    If you have experience with Objective-C, you may know that it provides two ways to store values and references as part of a class instance. In addition to properties, you can use instance variables as a backing store for the values stored in a property.

    Swift unifies these concepts into a single property declaration. A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. (emphasis is mine)

    Of course this applies only to using the "regular language" means, as opposed to using reflection: it might provide a way around this restriction, at the expense of readability.

    0 讨论(0)
提交回复
热议问题