プロパティラッパーは、プロパティに関するロジック(例えば、検証や変更、特定の方法での保存)をカプセル化し、コード内でのアノテーションを通じて異なるプロパティで再利用することを可能にするメカニズムです。これにより、繰り返しのコードを排除し、可読性を高めることができます。
プロパティラッパーは、@propertyWrapperアノテーションと必須プロパティwrappedValueを通じてプロパティラッパープロトコルを実装する構造体、クラス、または列挙型です。
制限と注意点:
例: 値の範囲を自動で制限するプロパティラッパー(Clamped)を作成してみましょう。
@propertyWrapper struct Clamped<Value: Comparable> { var value: Value let range: ClosedRange<Value> var wrappedValue: Value { get { value } set { value = min(max(newValue, range.lowerBound), range.upperBound) } } init(wrappedValue initialValue: Value, _ range: ClosedRange<Value>) { self.range = range self.value = min(max(initialValue, range.lowerBound), range.upperBound) } } struct Person { @Clamped(0...120) var age: Int = 25 } var p = Person() p.age = 200 // 現在 p.age = 120 p.age = -10 // 現在 p.age = 0
ラッパーオブジェクトにアクセスする方法は、構造体/クラスの外でどのように利用可能ですか?
回答: プロパティ名の前にアンダースコア(_)を付けてアクセスします。例えば、プロパティがageと呼ばれる場合、プロパティラッパーオブジェクトは_ageとして取得できます:
var p = Person() let wrapper = p._age // 型は Clamped<Int>
ストーリー
UserDefaultsを保存するプロジェクトでは、プリミティブ型で動作するカスタムプロパティラッパーが実装されました。参照型(クラス)に使用した際に予期しないメモリリークが発生しました — プロパティラッパーがオブジェクトへの強い参照を保持していたため、強い循環とデータリークに繋がったのです。エラーはラッパー内でweak/unowned参照に切り替えることで修正されました。
ストーリー
プロジェクトでは計算プロパティにプロパティラッパーを適用しようとしましたが、コンパイラはエラーを出しました:プロパティラッパーは保存プロパティにのみ使用可能です。この事実を見落としたため、モジュールの開発が2日遅れました。
ストーリー
ラッピング構造体を作成する際に、init(wrappedValue:...)を通じて正しい初期化構文を実装するのを忘れました。その結果、プロパティラッパーを介してデフォルト値を設定することができず、大規模なモデルにラッパーを統合した後に問題が明らかになりました。アーキテクチャを見直す必要がありました。