[Swift] PropertyWrapper 手を動かして理解する(その2 wrappedValue の初期化)

Swift

     
⌛️ 2 min.
PropertyWrapper 指定されている変数の初期化方法について説明します。

Swift [Swift] PropertyWrapper 手を動かして理解する(その1 wrappedValue)

初期値指定

propertyWrapper 指定された property の初期値設定について説明します。

property wrapper init 内

property wrapper の init で設定することができます。

example code


@propertyWrapper struct NoWrapInt {
  init() {
    self.wrappedValue = 0              // (1)
  }
  var wrappedValue: Int
}

struct CheckNoWrapInt {
  @NoWrapInt var value:Int             //(2)
}

var checkNoWrapInt = CheckNoWrapInt()  // (3)
print(checkNoWrapInt.value)            //(4)

(3)の struct が生成されるタイミングで、(1) の init が呼ばれ、初期値が設定されます。(4)で設定された初期値が、0 と表示されます。

ただ、この方法では、property wrapper を定義した箇所でのみ初期値を設定できることになります。少し不便です。

propertywrapper 修飾された 変数に初期値指定

通常の property と同様に、以下のように設定することができると直感的です。

example code


@NoWrapInt var variable:Int = 5

このようなコードでは、propertywrapper の init(wrappedValue:Int) が呼びされます。

# 定義がないとエラーになります。

早速追加して実行してみます。

example code


@propertyWrapper struct NoWrapInt {
  init(wrappedValue: Int) {
    self.wrappedValue = wrappedValue   // (1)
  }
  var wrappedValue: Int
}

struct CheckNoWrapInt {
  @NoWrapInt var value:Int = 4         // (2)
}

var checkNoWrapInt = CheckNoWrapInt()
print(checkNoWrapInt.value)            // (3)

(1) で渡された値を設定する initializer を定義します。
(2) が実行されるときに、initializer が呼び出され設定されますので、(3) では、4 と表示されます。

property wrapper の初期化指定

Property wrapper に対して直接的に初期値を指定することもできます。

例えば上記の例では、以下のように記述することで、wrappedValue の初期値を指定することができます。

example code


struct CheckNoWrapInt {
  @NoWrapInt var value1:Int = 5            // (1)
  @NoWrap(wrappedValue: 5) var value2:Int  // (2)
}

(1), (2) とも同じ動作をします。init(wrappedValue:Int) を呼び出しています。

もう少し凝った初期値指定

もう少し凝った初期値指定もできますが、現在の NoWrapInt では、シンプルすぎるので、もう少し複雑な property wrapper を定義します。

property wrapper “WrapIntLowerLimit”

特定の値より小さい値をセットできない property wrapper を定義します。

WrapIntLowerLimit


@propertyWrapper struct WrapIntLowerLimit {
  private var value: Int = 3
  private var lowerLimit:Int = 0
  var wrappedValue: Int {
    get {
      return value
    }
    set {
      self.value = max(newValue, 0)
    }
  }
}

lowerLimit より小さい値をセットしようとしても、セットできないようにする property wrapper です。
変数の初期値は、3 となっていて、チェックする境界値は 0 となっています。(意味的には、負の値がセットできないようにする property wrapper となっています)

動かしてみますと、

example code


@propertyWrapper struct WrapIntLowerLimit {
  private var value: Int = 3
  private var lowerLimit:Int = 0
  var wrappedValue: Int {
    get {
      return value
    }
    set {
      self.value = max(newValue, lowerLimit) // (7)
    }
  }
}

class CheckWrapInt {
  @WrapIntLowerLimit var value:Int
}

var cwi = CheckWrapInt()   // (1)
print(cwi.value)           // (2)
cwi.value = -4             // (3)
print(cwi.value)           // (4)
cwi.value = 12             // (5)
print(cwi.value)           // (6)

(1) の段階で、初期値がセットされていまて、(2) で、3 と表示されることで確認できます。(表示していませんが、lowerLimit は 0 です)

(3) で -4 を設定しようとしていますが、(7) のコードにて、0 が設定されますので、(4) でも 0 と表示されます。

(5) では、12 を設定しようとしていて、(7) のコードでもそのままの 12 が設定され、(6) では、 12 と表示されます。

このようになんらかの制御を組み込もうとするケースでは、変数としての初期値の他に、制御を指定する方法(このケースでは下限値をチェックする値)に対して、初期値を設定したくなります。

複数の値を設定したいのですから、これまでのような “var value:Int = 5” のような記述方法では設定できません。

property wrapper 指定での初期値指定

propertyWrapper 修飾で property を定義するときに、propertyWrapper に対しての初期値指定を行うことができます。

example code


class CheckWrapInt {
  @WrapIntLowerLimit var value:Int = 0                                // (1)
  @WrapIntLowerLimit(wrappedValue: 5, lowerLimit: -5) var value2:Int  // (2)
}

(2) のように propertyWrapper に対して、init のような設定を行うことができます。

少し長いですが、以下は、そのまま Plyaground で実行することができます。

example code


@propertyWrapper struct WrapIntLowerLimit {
  private var value: Int = 0
  private var lowerLimit:Int = 0
  init(wrappedValue:Int) {
    self.value = wrappedValue
  }
  init(wrappedValue: Int, lowerLimit:Int) {
    self.value = wrappedValue
    self.lowerLimit = lowerLimit
  }
  var wrappedValue: Int {
    get {
      return value
    }
    set {
      self.value = max(newValue, lowerLimit)
    }
  }
}

class CheckWrapInt {
  @WrapIntLowerLimit var value:Int = 0
  @WrapIntLowerLimit(wrappedValue: 5, lowerLimit: -5) var value2:Int // 変数値 5 境界値 -5 を初期値指定
}

var cwi = CheckWrapInt()
print(cwi.value)
cwi.value = -4
print(cwi.value)
cwi.value = 12
print(cwi.value)

print(cwi.value2)          // 初期値である 5 が表示される
cwi.value2 = -4            // lowerLimit(-5)より大きい値をセット
print(cwi.value2)          // -4 と表示される
cwi.value2 = -10           // lowerLimit(-5)より小さい値をセット
print(cwi.value2)          // -5 と表示される



まとめ

property wrapper の初期化
  • @WrapIntLowerLimit var value:Int = 0 のように変数に代入するように初期化できる
  • @WrapIntLowerLimit(wrappedValue: 5, lowerLimit: -5) var value2:Int のように、property wrapper に対して直接初期化も可能

ここまでが、propertyWrapper の最小限の説明ですが、ここまでの仕組みを使って、いろいろなアクセス制御を propertyWrapper できるようになります。

ですが、ここまでの説明に、State で有名になった “$” の説明がないことに気づいたかもしれません。

$ は、projectedValue という property と関連しています。次の記事で説明予定です。

説明は以上です。
不明な点やおかしな点ありましたら、ご連絡いただけるとありがたいです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です