21
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Swift】プロパティ・ラッパーの基本を理解する

Last updated at Posted at 2019-12-13

この投稿は何か?

Apple Swift Language GuideにあるProperty Wrappersに基づき、プロパティ・ラッパーの基本を理解します。

実行環境

macOS Catalina 10.15.2
Xcode11.3
Swift5.1

ハンズオン

小さな長方形

コードで、各辺の長さが12以下である小さな長方形を表現します。
2つのプロパティheightwidthを持つ構造体です。

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構造体と同じような記述を繰り返す必要があります。
繰り返している部分を抽出して、別の構造体に定義します。

12以下の数値を返す構造体
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 }
    }
}

SmallRectangleAnotherSmallRectangleに大した違いはありませんが、__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
}

上記、エラーの原因と修正対応について、みなさまどう思われるでしょうか?
ぜひ、お考えなどをお聞かせください。

21
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?