290

Swift には、C# と非常によく似たプロパティ宣言構文があります。

var foo: Int {
    get { return getFoo() }
    set { setFoo(newValue) }
}

ただし、アクションもありwillSetます。didSetこれらは、セッターが呼び出される前と後にそれぞれ呼び出されます。セッター内に同じコードを入れることができることを考えると、それらの目的は何ですか?

4

11 に答える 11

352

ポイントは、たとえば、プロパティが変更されたことを他のオブジェクトに通知するなど、自動ストレージいくつかの動作を持つプロパティが必要になる場合があることです。get/しかない場合はset、値を保持する別のフィールドが必要です。willSetとを使用するとdidSet、別のフィールドを必要とせずに値が変更されたときにアクションを実行できます。たとえば、その例では:

class Foo {
    var myProperty: Int = 0 {
        didSet {
            print("The value of myProperty changed from \(oldValue) to \(myProperty)")
        }
    }
}

myProperty変更されるたびに古い値と新しい値を出力します。ゲッターとセッターだけでは、代わりにこれが必要になります:

class Foo {
    var myPropertyValue: Int = 0
    var myProperty: Int {
        get { return myPropertyValue }
        set {
            print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
            myPropertyValue = newValue
        }
    }
}

したがってwillSetdidSet数行の節約と、フィールド リストのノイズの減少を表します。

于 2014-06-03T02:38:50.800 に答える
159

私の理解では、 set と get は計算されたプロパティ用です (格納されたプロパティからのバッキングはありません)

Objective-C から来ている場合は、命名規則が変更されたことを念頭に置いてください。Swift では、iVar またはインスタンス変数は格納されたプロパティと呼ばれます

例 1 (読み取り専用プロパティ) - 警告あり:

var test : Int {
    get {
        return test
    }
}

これにより、再帰的な関数呼び出しが発生するため、警告が発生します (getter 自体が呼び出されます)。この場合の警告は、「独自の getter 内で 'test' を変更しようとしています」です。

例 2. 条件付き読み取り/書き込み - 警告あり

var test : Int {
    get {
        return test
    }
    set (aNewValue) {
        //I've contrived some condition on which this property can be set
        //(prevents same value being set)
        if (aNewValue != test) {
            test = aNewValue
        }
    }
}

同様の問題 -セッターを再帰的に呼び出しているため、これを行うことはできません。また、初期化する保存されたプロパティがないため、このコードは初期化子がないことについて文句を言わないことに注意してください。

例 3. 計算されたプロパティの読み取り/書き込み - バッキング ストアを使用

これは、実際に保存されたプロパティの条件付き設定を可能にするパターンです

//True model data
var _test : Int = 0

var test : Int {
    get {
        return _test
    }
    set (aNewValue) {
        //I've contrived some condition on which this property can be set
        if (aNewValue != test) {
            _test = aNewValue
        }
    }
}

実際のデータは _test と呼ばれます (ただし、任意のデータまたはデータの組み合わせである可能性があります) _test は実際にはインスタンス変数であるため、初期値を提供する必要があることにも注意してください (または、init メソッドを使用する必要があります)

例 4. will と did セットの使用

//True model data
var _test : Int = 0 {

    //First this
    willSet {
        println("Old value is \(_test), new value is \(newValue)")
    }

    //value is set

    //Finaly this
    didSet {
        println("Old value is \(oldValue), new value is \(_test)")
    }
}

var test : Int {
    get {
        return _test
    }
    set (aNewValue) {
        //I've contrived some condition on which this property can be set
        if (aNewValue != test) {
            _test = aNewValue
        }
    }
}

ここでは、willSet と didSet が実際に保存されているプロパティの変更をインターセプトしていることがわかります。これは、通知の送信、同期などに役立ちます... (以下の例を参照)

例 5. 具体的な例 - ViewController コンテナ

//Underlying instance variable (would ideally be private)
var _childVC : UIViewController? {
    willSet {
        //REMOVE OLD VC
        println("Property will set")
        if (_childVC != nil) {
            _childVC!.willMoveToParentViewController(nil)
            self.setOverrideTraitCollection(nil, forChildViewController: _childVC)
            _childVC!.view.removeFromSuperview()
            _childVC!.removeFromParentViewController()
        }
        if (newValue) {
            self.addChildViewController(newValue)
        }

    }

    //I can't see a way to 'stop' the value being set to the same controller - hence the computed property

    didSet {
        //ADD NEW VC
        println("Property did set")
        if (_childVC) {
//                var views  = NSDictionaryOfVariableBindings(self.view)    .. NOT YET SUPPORTED (NSDictionary bridging not yet available)

            //Add subviews + constraints
            _childVC!.view.setTranslatesAutoresizingMaskIntoConstraints(false)       //For now - until I add my own constraints
            self.view.addSubview(_childVC!.view)
            let views = ["view" : _childVC!.view] as NSMutableDictionary
            let layoutOpts = NSLayoutFormatOptions(0)
            let lc1 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("|[view]|",  options: layoutOpts, metrics: NSDictionary(), views: views)
            let lc2 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
            self.view.addConstraints(lc1)
            self.view.addConstraints(lc2)

            //Forward messages to child
            _childVC!.didMoveToParentViewController(self)
        }
    }
}


//Computed property - this is the property that must be used to prevent setting the same value twice
//unless there is another way of doing this?
var childVC : UIViewController? {
    get {
        return _childVC
    }
    set(suggestedVC) {
        if (suggestedVC != _childVC) {
            _childVC = suggestedVC
        }
    }
}

計算されたプロパティと保存されたプロパティの両方の使用に注意してください。同じ値が 2 回設定されるのを防ぐために、計算されたプロパティを使用しました (悪いことが起こらないようにするためです!)。私は willSet と didSet を使用して、viewControllers に通知を転送しました (UIViewController のドキュメントと viewController コンテナーに関する情報を参照してください)。

これがお役に立てば幸いです。ここで間違いを犯した場合は、誰かが叫んでください!

于 2014-06-26T11:51:21.663 に答える
21

これらはプロパティ オブザーバーと呼ばれます。

プロパティ オブザーバーは、プロパティの値の変化を観察して対応します。プロパティ オブザーバーは、新しい値がプロパティの現在の値と同じであっても、プロパティの値が設定されるたびに呼び出されます。

抜粋: Apple Inc.「The Swift Programming Language」。アイブック。https://itun.es/ca/jEUH0.l

これは、UI 要素とのデータ バインディング、プロパティ変更の副作用のトリガー、同期プロセスのトリガー、バックグラウンド処理など、従来KVOで行っていたことを可能にするためだと思います。

于 2014-06-03T02:53:14.577 に答える
20

ノート

willSetdidSet委任が行われる前にプロパティが初期化子に設定されている場合、オブザーバーは呼び出されません

于 2015-08-18T10:09:49.170 に答える
5

プロパティに新しい値が割り当てられるたびに、プロパティの willSet および didSet オブザーバー。これは、新しい値が現在の値と同じであっても当てはまります。

willSetまた、回避するにはパラメーター名が必要ですが、そうでdidSetはないことに注意してください。

プロパティの値が更新された後、didSet オブザーバーが呼び出されます。古い値と比較します。ステップの総数が増えた場合は、新しいステップが何回実行されたかを示すメッセージが出力されます。didSet オブザーバーは、古い値のカスタム パラメーター名を提供せず、代わりに oldValue の既定の名前が使用されます。

于 2015-06-13T08:08:05.973 に答える
-5

私はC#を知りませんが、少し当て推量で理解できたと思います

foo : int {
    get { return getFoo(); }
    set { setFoo(newValue); }
}

します。Swift にあるものと非常によく似ていますが、同じではありません。Swift にはgetFooandがありませんsetFoo。これは小さな違いではありません。つまり、値の基礎となるストレージがないことを意味します。

Swift には、格納され計算されたプロパティがあります。

計算されたプロパティはget、持っている可能性がありますset(書き込み可能な場合)。しかし、ゲッターとセッターのコードは、実際にデータを保存する必要がある場合、他のプロパティでそれを行う必要があります。バッキングストレージはありません。

一方、格納されたプロパティにはバッキング ストレージがあります。しかし、 と はありませ。代わりに、変数の変更を観察し、最終的に副作用をトリガーしたり、保存された値を変更したりするために使用できます。計算されたプロパティにはとはありません。また、計算されたプロパティの場合はコードを使用して変更を制御できるため、それらは必要ありません。getsetwillSetdidSetwillSetdidSetset

于 2014-06-05T08:08:42.917 に答える