この投稿は何か?
Apple Swift Language GuideにあるProperty Wrappersに基づき、プロパティ・ラッパーの基本を理解します。
実行環境
macOS Catalina 10.15.2
Xcode11.3
Swift5.1
ハンズオン
小さな長方形
コードで、各辺の長さが12
以下である小さな長方形を表現します。
2つのプロパティheight
とwidth
を持つ構造体です。
struct SmallRectangle {
private var _height = 0
private var _width = 0
var height: Int {
get { return _height }
set { _height = min(newValue, 12) }
}
var width: Int {
get { return _width }
set { _width = min(newValue, 12) }
}
}
高さも幅も、計算プロパティを利用して、プロパティに12
より大きい値が割り当てられた場合だけ、12
を返すようにしています。
実際に、インスタンスを生成してみます。
var rectangle = SmallRectangle()
rectangle.height = 9
rectangle.height // height is 9.
長方形の高さは9
になりました。
次に、長方形の高さを15
に変更してみます。
rectangle.height = 15
rectangle.height // height was assigned 15, but now is 12.
割り当てようとした高さが大きすぎたので、計算プロパティによって12
が割り当てられました。
コードを分離する
では、次に「小さな円」や「小さな三角形」の構造体を定義する必要があった場合はどうでしょう?
いずれも単純な構造体ですが、SmallRectangle
構造体と同じような記述を繰り返す必要があります。
繰り返している部分を抽出して、別の構造体に定義します。
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
TwelveOrLess
構造体は割り当てた数値が12
より大きかった場合、12
を返します。
このTwelveOrLess
構造体を利用して、各辺のサイズが12
以下の長方形を表現する構造体を定義します。
struct AnotherSmallRectangle {
private var _height = TwelveOrLess()
private var _width = TwelveOrLess()
var height: Int {
get { return _height.wrappedValue }
set { _height.wrappedValue = newValue }
}
var width: Int {
get { return _width.wrappedValue }
set { _width.wrappedValue = newValue }
}
}
SmallRectangle
とAnotherSmallRectangle
に大した違いはありませんが、__12以下を返す__部分のコードを再利用できるようになりました。
「直径または半径が12
以下の円」や「高さが12
以下の三角形」の構造体を定義する際に、TwelveOrLess
構造体を再利用できます。
@propertyWrapper
ここで、プロパティ・ラッパーを使用すると、コードを簡略化できます。
プロパティ・ラッパーを使用するには、TwelveOrLess
構造体の定義直前に@propertyWrapper
キーワードを記述します。
@propertyWrapper // add keyword
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
プロパティがラップされた長方形の構造体は、次のように定義されます。
プロパティの直前に@TwelveOrLess
属性を指定します。
struct SmallRectangleWithPropertyWrapper {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
すごくシンプルに記述できるようになりました。
同じように動作します。
var simpleRectangle = SmallRectangleWithPropertyWrapper()
simpleRectangle.height = 7
simpleRectangle.height // height is 7
simpleRectangle.height = 17
simpleRectangle.height // height is 12
ただ、Playgroundで実行している限りですが、動作が不安定で、以下のようなエラーが発生することがあります。
error: MyPlayground.playground:58:17: error: 'height' is inaccessible due to 'internal' protection level
原因が思い当たらないのですが、次のように修正すると確実に動作するようになります。
struct SmallRectangleWithPropertyWrapper {
@TwelveOrLess public var height: Int // add public keyword
@TwelveOrLess public var width: Int // add public keyword
}
上記、エラーの原因と修正対応について、みなさまどう思われるでしょうか?
ぜひ、お考えなどをお聞かせください。