kazuki’s blog

新人エンジニアのブログ

Swiftのプロパティ監視willset/didset

こんにちは!かずきです。   今回はSwiftの特徴的機能の一つ、プロパティ監視のdidset/willsetについて書いていきたいと思います。 前回に続き、Swift入門者向けの記事です。  

 プロパティとは

private var hoge: Int = 0

public var Hoge: Int{
    set{
          self.hoge = newValue
    }
    get{
         return self.hoge
     }
 }

読み書きそれぞれ別の範囲でのアクセス制限を掛けたい場合などのアクセス制御に使用するものです。
上のようにsetで囲んで値を設定するsetterとget囲んで値を渡すgetterの2種類があります。

class HogeHoge
{
    private var hoge: Int = 0

    public var Hoge: Int{
        get{
              return self.hoge
        }
    }
}

このようなプロパティを持つクラスがあるとして、次のようにアクセスすると。。。

let hogehoge = HogeHoge()

print("\(hogehoge.Hoge)")   // "0"

// エラー
hogehoge.Hoge = 1

値の取得には成功しますが、代入は失敗してしまいます。
このように外部からは取得しか出来ず、書き換えさせない場合や複数の変数の計算結果などを返したい場合によく利用されます。

プロパティとプロパティ監視の違い

プロパティは変数のアクセス制御などに使用するのに対し、プロパティ監視は変数の変更を監視するものになります。
なぜ変数の監視なのにプロパティ監視なのかと言うと、Swiftでは変数もプロパティの1種として扱われているからです。
先程説明したプロパティと変数としてのプロパティの違いは次に説明します。

2種類のプロパティ

Swiftにコンピューテッドプロパティストアドプロパティの2種類のプロパティがあります。

  • コンピューテッドプロパティ
    先ほど紹介したものはこちらです。
    プロパティそのものには値を格納出来ません
    よくプロパティと呼ばれるものはこちらになります。

  • ストアドプロパティ
    一般的に変数と呼ばれるのもです。
    getter setterは利用出来ませんが、 こちらのプロパティには値を格納出来ます

Swiftではこのように2種類のプロパティとして扱われています。
説明はしましたが、Swiftでは変数もプロパティの一種くらいの認識で大丈夫だと思います。

プロパティ監視(willset / didset)

プロパティ監視は先程説明した通り変数の変更を監視するものです。

  • willset ・・・変数の値の変更前に通知される
  • didset ・・・変数の値の変更後に通知される
var hoge:Int = 0 {
    willSet(newValue){
         hoge = 0 // 値は変わらない
    }
    didSet(oldValue){
         if(hoge < oldValue)
         {
                print("元の値より小さい値は代入できません。\(oldValue) -> \(hoge)")
                hoge = oldValue // 値は変わる(この場合willset / didsetには通知されない)
        }
    }
}

willsetで自身の変数に代入しても値は変化しませんが、didset内で代入した場合は値が変化します
didset内で自身の変数を変化させた場合willset didsetには通知されません。

プロパティ監視の使い所

先程の説明で処理の場合、setterでも同じ内容の事をする事が出来ます。
主なプロパティ監視の使いどころしては、値が変化された場合に特定のメソッドを呼ぶ イベントで通知するなどです。

まとめ

ここまで説明してきましたが、基本的にwillset / didsetで出来る処理はgetter / setterでも出来ます。 ですが。。。

  • 読み書きそれぞれ別の範囲でのアクセス制限を掛けたい場合はgetter setterで処理を記述する。

  • 値が変更された場合に処理を行いたい場合willset didsetで処理を記述する。

このような使い分けで利用する事により、 変数(ストアドプロパティ)プロパティ(コンピューテッドプロパティ)両方にアクセス可能な場所で setterに必要な処理を書いているので変数に代入してしまって正しい処理がされないなどのリスクを回避する事が出来ます。

他にも使い分け方があれば是非教えて下さい!