Swiftのプロパティには、プロパティラッパーという機能が用意されています。
今回は、Swiftのプロパティラッパーについて以下の観点で解説します。
- プロパティラッパーとは何か
- プロパティラッパーの使い方
- プロパティーラッパーにおけるプロパティの初期化
- プロパティーラッパーの$プロパティ
参考本
[改訂新版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus
プロパティラッパーとは何か
プロパティラッパーとは、プロパティの値の保存方法や計算方法をプロパティラッパーとして定義し、同じ方法を適用できるプロパティを、そのプロパティラッパーでラップすることで開発の効率化を図るというもの
です。
プロパティラッパーは、構造体やクラス、列挙型として定義することができます。
プロパティラッパーの使い方
それでは、コードを見ながら具体的にプロパティラッパーの使い方について解説します。
まず、以下のコードを見てください。
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
これは、12より大きい数字は12にして返すというプロパティラッパーを構造体として定義したものです。
プロパティラッパーによってラップされた値をwrapped valueといいます。
プロパティラッパーは構造体やクラス、列挙型に@propertyWrapperをつけることで定義することができます。
また、プロパティの値をどう保存するのか、あるいは、計算するのか、その方法を、wrappedValueに対して定義します。
この例の場合、その方法を
get { return number }
set { number = min(newValue, 12) }
}
として定義しています。
続いて、プロパティラッパーでプロパティをラップする方法について見ていきましょう。
次のコードを見てください。
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
この例のように、プロパティの前にプロパティラッパー名を属性(@)としてつけることで、プロパティをプロパティラッパーでラップすることができます。
この例の場合、上で定義したプロパティラッパーTwelveOrLessを、プロパティheightとwidthに以下のように適用しています。
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
このように、プロパティラッパーを適用することで、そこで定義された値の保存方法や計算方法がプロパティに適用されます。
例えば、以下の例のように、12より大きい数字は12にして返すというTwelveOrLessの方法が適用されます。
print(rectangle.height)
// “0”と出力
rectangle.height = 10
print(rectangle.height)
// “10”と出力
rectangle.height = 24
print(rectangle.height)
// “12”と出力
プロパティーラッパーにおけるプロパティの初期化
プロパティーラッパーを意図的に初期化したい場合は、イニシャライザー(init)を使います。
次の例を見てください。
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
このプロパティラッパーSmallNumberには3つのイニシャライザー、init()、init(wrappedValue: Int)、init(wrappedValue: Int, maximum: Int) が定義されています。
次に、初期化の方法について見ていきましょう。
以下の例を見てください。
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// “0 0”と出力
この例の場合、イニシャライザーinit()が適用されています。
次に、以下の例の場合、
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// “1 1”と出力
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
によって、イニシャライザーinit(wrappedValue: Int)が適用されています。
最後に、以下の例の場合、
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// “2 3”と出力
narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// “5 4”と出力
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
によって、イニシャライザーinit(wrappedValue: Int, maximum: Int) が適用されていることがわかります。
プロパティーラッパーの$プロパティ
次に、プロパティラッパーの射影について説明します。
プロパティーラッパーは、プロパティを、その値の保存方法や計算方法でラップすることでwrapped valueを返すようにするという説明をしてきました。
プロパティーラッパーには、このwrapped valueだけでなく、それを射影したprojected valueを返す付加的な機能があります。
次の例を見てください。
struct SmallNumber {
var number: Int
var projectedValue: Bool
init() {
self.number = 0
self.projectedValue = false
}
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
}
この例は、
12より大きい数字は12にして返すというwrapped valueだけでなく
その処理が行われたかどうかをブール値として返すprojected valueを定義してます。
projected valueを確認したい場合は、以下の例のように、プロパティーラッパーでラップされたプロパティ名の前に$マークをつけることで確認することができます。
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// “false”と出力
someStructure.someNumber = 55
print(someStructure.$someNumber)
// “true”と出力
この例の場合、$someNumberを指定することでprojected valueを取得しています。
以上、今回はSwiftのプロパティラッパ−について解説しました。