Swift:如何更改属性';s值,而不调用其didSet函数

Swift:如何更改属性';s值,而不调用其didSet函数,swift,properties,didset,Swift,Properties,Didset,如何在Swift中设置属性值,而不在初始化上下文之外调用其didSet()函数?下面的代码是在类的noside()函数中实现这一点的失败实验 class Test { var toggle : Bool = 0 var hoodwink : Int = 0 { didSet(hoodwink) { toggle = !toggle } } // failed attempt to set witho

如何在Swift中设置属性值,而不在初始化上下文之外调用其
didSet()
函数?下面的代码是在类的noside()函数中实现这一点的失败实验

class Test
{
    var toggle : Bool = 0
    var hoodwink : Int = 0 {
        didSet(hoodwink)
        {
            toggle = !toggle
        }
    }

// failed attempt to set without a side effect

    func noside(newValue : Int)
    {
        hoodwink = newValue
        println("hoodwink: \(hoodwink) state: \(toggle)")
    }

    func withside(newValue : Int)
    {
        self.hoodwink = newValue
        println("hoodwink: \(hoodwink) state: \(toggle)")
    }
}
在具有自动合成属性的Objective-C中执行此操作非常简单:

有副作用(如果在setter中存在):

无副作用:

_hoodwink = newValue;

在Objective-C中,为了“避免副作用”,您要做的是访问属性的备份存储—它的实例变量,默认情况下,它的前缀是下划线(您可以使用
@synthesis
指令来更改它)

然而,Swift语言设计师似乎特别小心,使其无法访问属性的支持变量:根据该书

如果您有使用Objective-C的经验,您可能知道它提供了两种方法来将值和引用存储为类实例的一部分。除了属性之外,还可以使用实例变量作为属性中存储的值的后备存储

Swift将这些概念统一到单个属性声明中Swift属性没有相应的实例变量,并且属性的备份存储区不能直接访问。(重点是我的)


当然,这只适用于使用“常规语言”的方法,而不是使用反射:它可能提供一种绕过此限制的方法,而以牺牲可读性为代价。

一种可能的解决方法是提供一个绕过didSet的setter

 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
        }
    }

丑陋但可行

如果您确切知道何时需要应用副作用,请明确说明:

1解决方案:

func noside(newValue : Int) {
    hoodwink = newValue
}

func withside(newValue : Int) {
    self.hoodwink = newValue
    toggle = !toggle
}
var toggle : Bool = false
var hoodwink : Int = 0 
var hoodwinkToggle: Int {
    get { return hoodwink }
    set(newValue) {
        hoodwink = newValue
        toggle = !toggle
    }
}
2解决方案:

func noside(newValue : Int) {
    hoodwink = newValue
}

func withside(newValue : Int) {
    self.hoodwink = newValue
    toggle = !toggle
}
var toggle : Bool = false
var hoodwink : Int = 0 
var hoodwinkToggle: Int {
    get { return hoodwink }
    set(newValue) {
        hoodwink = newValue
        toggle = !toggle
    }
}
  • func setHoodwinkWithToggle(hoodwink:Int){…}

  • 我认为这些解决方案将更加清晰易读,然后使用一个变量,在某些调用中应该有副作用,而在其他调用中不应该有副作用

    我试图解决的问题是,当一个值更改时,或者如果两个值同时更改时,我希望得到一个
    docomonupdate
    调用。这样做会创建一个递归,因为如果其中一个值发生更改,每次都会调用
    didSet
    。示例设置,我的更为复杂:

    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
    
    注意:由于注释掉的行,Wilma不会打印

    我的解决方案是将所有这些变量移动到一个单独的结构中。这种方法解决了这个问题,并具有创建另一个有助于理解含义的分组的副作用。当其中一个值独立更改并且两个值同时更改时,我们仍然会得到
    docomonsetup

    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
    

    对。附录:如果你想管理对一个属性的“后备存储”的访问,你可以使用一个单独的、私有的、存储的属性。非常聪明。我会选择
    var triggerobservators=true
    我自己,以避免双重否定。