24

如何在 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 中):

self.hoodwink = newValue;

无副作用:

_hoodwink = newValue;
4

4 回答 4

14

一个可能的破解方法是提供一个绕过你的 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
        }
    }

丑陋但可行

于 2014-12-13T19:25:21.647 回答
10

您在 Objective-C 中“避免副作用”所做的是访问属性的后备存储 - 它的实例变量,默认情况下带有下划线前缀(您可以使用@synthesize指令更改它)。

然而,看起来 Swift 语言设计者特别注意使访问属性的支持变量变得不可能:根据这本书,

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

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

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

于 2014-08-19T23:06:19.297 回答
1

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

1 解决方案:

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

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

2 解决方案:

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. ……

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

于 2014-12-13T20:43:44.840 回答
0

我试图解决的问题是我想doCommonUpdate在一个值更改时接到一个电话,或者如果两个值同时更改时接到一个电话。这样做会产生递归,因为如果任何一个值发生变化,它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 不会打印。

我的解决方案是将所有这些变量移动到一个单独的结构中。这种方法解决了这个问题,并具有创建另一个有助于意义的分组的附带好处。我们仍然会得到doCommonSetup两个值独立变化以及两个值同时变化的时间。

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
于 2018-05-14T07:39:06.570 回答