【Swift】プロパティ総まとめ~公式ドキュメントをもとに全部まとめてみた~


#プロパティ
そもそもプロパティとは、なんぞやというところから

classやstruct,enumでよく見るこいつです。

```Swift:class
class{
  let men: String = "男"
}
```
このように、プロパティは型（クラス、構造体、列挙型）を構成する要素の一つで、型もしくは型のインスタンスに紐づいた値を指します。
（ちなみに、型を構成する他の要素には、メソッド・イニシャライザ・ネスト・サブスクリプトなどがあります。）


本記事では、[プロパティの公式ドキュメント](https://docs.swift.org/swift-book/LanguageGuide/Properties.html)の内容を網羅してまとめていきます！

本記事の構成

- **Stored Properties(ストアド・プロパティ)**
    -  Lazy Stored Propertiesz(レイジー・ストアドプロパティ)
- **Computed Properties(コンピューテッド・プロパティ)**
- **Property Observers(プロパティオブザーバー)**
- **Property Wrapper（プロパティラッパー）**
- **Type Property**


# 値を保有する  ストアド・プロパティ 

ストアド(Stored)は、「保有した」という意味です。
値には定数と変数があります。

- letキーワード：定数を保持
- varキーワード：変数の保持

```Swift:StorerdProperties
struct StoredProperity{
  let test: Int 
  var test2: String
}
```

上記のようなものをよく見るパターンですね。
このストアド・プロパティを細分化すると**レイジー・ストアドプロパティ**なるものがあります。

### アクセスされるまで初期化を遅延させる レイジー・ストアドプロパティ

- 初期値が、外部要因に依存している場合（インスタンスの初期化が完了するまで値がわからない）
- 初期値に不必要(or 必要になるまで実行しなくて良い)で複雑な計算量が多いセットアップをする場合

などで役に立ちます!
`lazy var ~`のようにlazy修飾子をつけます。
再代入ができないletキーワードにはもちろん使えません。

```Swift:lazyStoredProperty
class data{
  var fileName = "data.txt"
}

class getData{
  lazy var importer = data()
  var data = [String]()
}

let lazyStoredProperty = getData() //ここでは、まだimporterは初期化されない
print(lazyStoredProperty.importer)  //ここで初めてimporterにアクセスして初期化が行われる
```

## 値の変化を観察・応答する　プロパティ・オブザーバ
**レイジー以外のストアド・プロパティ**を対象に追加できます。
プロパティ・オブザーバを追加することで、値の変化前・変化後それぞれのタイミングで処理応答をすることができます。

具体的には、
**willSet**→値が格納される直前に呼び出されます。新しい値はデフォルトで**newValue**
**didSet**→新しい値が格納された直後に呼び出されます。古い値はデフォルトで**oldValue**

```Swift
class test{
  var A:Int = 0 {
      willSet(newValue){
        print("値が\(newValue)に更新されます。")
      }
      didSet {
        print("\(A)が新しい値で、\(oldValue)が古い値です。")
      }
  }
}
```


# 値を保有しない計算型　コンピューテッド・プロパティ
コンピューテッド(computed)は、「計算された」という意味です。
**ゲッター/セッター**を用いて、他のプロパティーの値から間接的に値を取得・設定する。

ゲッター・セッターについて簡単に記述すると、

- ゲッター
    - 他のプロパティの値を元に処理をし、自身のプロパティの値を取得する(処理が単一の場合returnを省略できる)
- セッター
    - 自身の新しい値（デフォルトでは、「newValue」）を元に処理をし、他のプロパティの値を更新する

基本的に**ゲッターは必須だが、セッターは任意**です。
セッターがない場合は、読み取り専用コンピューテッド・プロパティと呼ばれます。

```Swift
class computedProperty {
  var name = "田中さん"
  var circumstance = "田中さんはget待ちだ"
  var GetSet: String {
    get {
      if name == "田中さん"{
        return "田中さんをGetした"
      }
    set {
      if newValue == "田中さんをGetした"{
        circumstance = "「田中さんはGetされた」とsetされた"
    }
}
```
      

#複数のプロパティの処理を管理する プロパティ・ラッパー

プロパティラッパーは、一言でういと**プロパティに関する処理を他の型に移譲するもの**です。
このプロパティラッパーをつかうことによって、個別の実装ケースに対してハードコーディングする必要がなくなります。

#### プロパティラッパーの使い方

- Property Wrapper にしたい型に`@propertyWrapper`をつける
- Property Wrapper 型には`wrappedValue`というインスタンスプロパティを持つ
- Property Wrapper を使用したい他の型のプロパティの頭に`@propertyWrapperの型名`をつける

#### プロパティラッパーを使った場合

```Swift:UsingPropertyWrapper
import UIKit

var str = "Hello, playground"

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init(){self.number = 0}
    var wrappedValue:Int {  //このwrappedValueが
        get {return number}
        set {number = min(newValue, 12)} // 上限値を12とする
    }
}

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var weight: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)  // 出力：0

rectangle.height = 10
print(rectangle.height)  //出力：10

rectangle.height = 14
print(rectangle.height)  //出力：12

```

#### プロパティラッパーを使わなかった場合

```Swift:NotUsingPropertyWrapper
import UIKit

var str = "Hello, playground"

struct TwelveOrLess {
    private var number: Int
    init(){self.number = 0}
    var wrappedValue:Int {
        get {return number}
        set {number = min(newValue, 12)} // 上限値を12とする
    }
}

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _weight = TwelveOrLess()
    var height: Int{
        get {return _height.wrappedValue}
        set {_height.wrappedValue = newValue}
    }
    var weight:Int {
        get {return _weight.wrappedValue}
        set {_weight.wrappedValue = newValue}
    }
}

var rectangle = SmallRectangle()
print(rectangle.height)  // 出力：0

rectangle.height = 10
print(rectangle.height)  //出力：10

rectangle.height = 14
print(rectangle.height)  //出力：12
```

このように、**複数のプロパティに対して同様な処理を行いたい場合などでは、コードをすっきりさせることができます!**

今回記事で触れた部分はごく一部に過ぎず、プロパティラッパーはまだまだ奥が深いです。
もっと詳しく勉強したい方はプロポーザルを見ることをお勧めします！
[プロパティラッパーのプロポーザル](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md)


# 型自身に紐づく タイププロパティ(スタティックプロパティ・クラスプロパティ)


#### インスタンスプロパティ・スタティックプロパティ・クラスプロパティ

- インスタンスプロパティ：インスタンスのプロパティにアクセス
- スタティックプロパティ：`staticキーワード`を用いることで、その型自身とプロパティを紐付ける。サブクラスでオーバーライドは不可能
- クラスプロパティ：`classキーワード`を用いることで、クラス自身に紐付ける。サブクラスでオーバーライドが可能


```Swift:instanceProperty
struct staticProperty {
 var name: String = "Aさん" // インスタンスプロパティ
 static var name2:String = "Bさん"  //スタティックプロパティ
}

let staticPropertyInstance = A()
print(staticPropertyInstance.name)  //出力：Aさん
print(staticProperty.name) //エラー

print(staticPropertyInstance.name2)  //エラー
print(staticProperty.name2)  //出力：Bさん

class classProperty {
  var name:String = "Cさん"  //インスタンスプロパティ
  class var name2:String = "ClassCさん"  //クラスプロパティ
}

let classPropertyInstance = classProperty()
print(classPropertyInstance.name)  //Cさん
print(classProperty.name)  //エラー

print(print(classPropertyInstance.name2)  //エラー
print(classPropertyInstance.name2)  //ClassCさん