web-dev-qa-db-ja.com

Swift:didSet関数を呼び出さずにプロパティの値を変更する方法

初期化コンテキストの外部でdidSet()関数を呼び出さずに、Swiftでプロパティの値を設定するにはどうすればよいですか?以下のコードは、クラスの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で行うのは非常に簡単です。

副作用あり(セッターに存在する場合):

self.hoodwink = newValue;

副作用なし:

_hoodwink = newValue;
20
ctpenrose

「副作用を回避する」ためにObjective-Cで行うことは、プロパティのバッキングストア(デフォルトではアンダースコアがプレフィックスとして付けられているインスタンス変数)にアクセスすることです(これは@synthesizeディレクティブを使用して変更できます)。

ただし、Swift言語設計者は、プロパティのバッキング変数にアクセスできないように特別な注意を払ったようです。本によると、

Objective-Cの経験がある場合は、クラスインスタンスの一部として値と参照を格納する2つの方法を提供していることをご存知かもしれません。プロパティに加えて、プロパティに格納されている値のバッキングストアとしてインスタンス変数を使用できます。

Swiftは、これらの概念を単一のプロパティ宣言に統合します。 A Swiftプロパティには対応するインスタンス変数がなく、プロパティのバッキングストアに直接アクセスされません。(強調は私のものです)

もちろん、これは、リフレクションを使用するのではなく、「正規言語」手段を使用する場合にのみ適用されます。読みやすさを犠牲にして、この制限を回避する方法を提供する可能性があります。

7
dasblinkenlight

これを回避するためのハックの可能性は、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
        }
    }

醜いが実行可能

8
Warren Burton

いつ副作用を適用したいかを正確に知っている場合は、明示的にそれを作成してください。

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

これらの解決策はより明確で読みやすいと思います。1つの変数を使用すると、ある呼び出しでは副作用が発生し、他の呼び出しでは発生しないはずです。

0
Mikhail

私が解決しようとしていた問題は、1つの値が変更されたときに、または両方が同時に変更された場合に1つ、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
0
possen