【Swift】型の設計指針について

Swiftの特徴の一つとして、
**構造体や列挙型、プロトコルの表現力が豊かである**ことが挙げられます。

また、オプショナル型やlet、varキーワードによって
**型のプロパティの特性も細かくコントロールできます。**

これらの表現力の向上に伴って、
型を設計する際の選択肢が広がりました。

こうした多くの選択肢からどのような指針で何を選べば良いか説明します。

#クラスと構造体

結論からいうと、**基本的に構造体を使うべきだと思います。**

Swiftではクラスで実現可能なことの大半は構造体で実現できます。
なので、型を設計するたびに、クラスにすべきか構造体にすべきかを検討することになります。

ただ実際は、**Swiftの標準ライブラリの型のほとんどが構造体で定義されています。**
つまり、Swiftでは構造体を積極的に利用した設計が推奨されています。

**構造体で設計することが困難な場合に初めてクラスでの実装を検討すべきです。**

##クラスがもたらすバグ

基本的には構造体を・・・と言いましたがなぜそうなのか。
クラスがダメな理由について説明します。

下記のサンプルコードは、
日本の気温を２５°、エジプトの気温を４０°にする予定だったコードです。

```swift

class Tempereture {
    var celsius: Double = 0.0
}

class Country {
    var temperature: Tempereture
    init(temperature: Tempereture) {
        self.temperature = temperature
    }
}

let temperature = Tempereture()
temperature.celsius = 25
let Japan = Country(temperature: temperature)
temperature.celsius = 40
let Egypt = Country(temperature: temperature)
Japan.temperature.celsius   // 40
Egypt.temperature.celsius   // 40
```

なぜどちらも40°になってしまうのか。
それはクラスが**参照型**であることに起因します。

参照型では、インスタンスが引数として渡された時、
そのインスタンスの参照が渡されます。

つまり、JapanもEgyptも同じインスタンスを参照しているのです。

なので、temperatureプロパティの値を変更すると
**両方のインスタンスに影響してしまいます。**

とはいえ、このようなミスは初歩的で気づくことが多いですが、
もう少し複雑なコードになってくるとどうでしょうか。

下記のサンプルコードでは、非同期処理を使っています。
**非同期処理とは、通常の処理とは別のスレッドで処理を行うことです。**

通常は、重い処理を行っている場合それが終わらないと次の処理にいけません。
なので、ユーザーに待ち時間が生まれてしまいます。

**そのような事態を防ぐために通常の処理とは別のスレッドで処理を行い、
本来の処理の妨げにならないようにしたりします。**
(非同期処理については後日記事を載せる予定です。)

```swift

import Dispatch

class Tempereture {
    var celsius: Double = 0.0
}

let temperature = Tempereture()
temperature.celsius = 25

//別スレッドでtemperatureの値を編集
let dispatch = DispatchQueue.global(qos: .default)

dispatch.async {
    temperature.celsius += 1
}

temperature.celsius += 1
temperature.celsius   // 27
```

`dispatch.async { }`の部分が非同期処理になります。
流れとしては、`let dispatch = DispatchQueue.global(qos: .default)`までは
一つのスレッドで実行されます。

`dispatch.async`で、
本来の処理を進めていくスレッドAと`dispatch.async`内の処理を行うスレッドBに別れます。

`dispatch.async { }`内の処理が終了したら本来の処理(スレッドA)と合流します。

`temperature.celsius += 1`は２つのスレッドで行われ、
`temperature.celsius`の値は２７になります。

では、次のサンプルコードをご覧ください。

```swift

import Dispatch
var count = 0

class Tempereture {
    var celsius: Double = 0.0
}

let temperature = Tempereture()
temperature.celsius = 25

//別スレッドでtemperatureの値を編集
let dispatch = DispatchQueue.global(qos: .default)

dispatch.async {
    print("dispatchの処理を行います。")
    for _ in 0...4 {
        count += 1
        print(count)
    }
    temperature.celsius += 1
}

temperature.celsius += 1
print(temperature.celsius)

実行結果
dispatchの処理を行います。
26.0
1
2
3
4
5
```

このコードは先ほどと少し違い、for-in文が追加されています。
そして`temperature.celsius`の出力結果が２６になっています。

これはなぜかというと、`dispatch.async { }`内の処理で、
`temperature.celsius += 1`にたどり着く前に値を出力しているからです。

実行結果を見ると分かる通り、
「dispatchの処理を行います。」が出力された後に
`temperature.celsius`が出力されています。

つまりfor-in文を読み込んでいる時に出力されたことになります。

このような処理がいろいろな箇所で行われると、
参照型であるクラスは、どのタイミングで値が変わるかがわからなくなります。

**コードの一部を見ただけで結果を推論することは困難になります。**
このようなクラスの特性は、バグの温床となりがちです。

##値型の構造体がもたらす安全性
一方で構造体は値型です。

なので、インスタンスが引数として渡される時、
その参照ではなく値そのものが渡されます。

先ほどのサンプルコードをクラスから構造体に変えてみます。

```swift

struct Tempereture {
    var celsius: Double = 0.0
}

struct Country {
    var temperature: Tempereture
    init(temperature: Tempereture) {
        self.temperature = temperature
    }
}

var temperature = Tempereture()
temperature.celsius = 25
let Japan = Country(temperature: temperature)
temperature.celsius = 40
let Egypt = Country(temperature: temperature)
Japan.temperature.celsius   // 25
Egypt.temperature.celsius   // 40
```

今度は期待通りの結果になりました。

**Temperature型のインスタンスは、受け渡されるたびにコピーされる**ので
それぞれのCountry型のインスタンスは別々のTemperature型のインスタンスを保持しています。

**別の見方をすれば、
ある構造体の所有者は常に１つであることが保証されているということです。**

構造体の持つこうした特性により、クラスとは異なりコードの実行結果が容易に予測できます。

この構造体の持つ安全性こそが、
Swiftで構造体の利用が推奨される理由になります。

##クラスを利用すべき時
ここまでは構造体を利用した設計が重視されていることを説明してきました。
しかし、これは**クラスが不要であることを意味する訳ではありません。**

次のような場合にはクラスを利用する必要があります。
**・参照を共有する必要がある
・インスタンスのライフサイクルに合わせて処理を実行する**

###参照を共有する

参照を共有することによって、
**ある箇所での操作を他の箇所へ共有させる時はクラスが適しています。**

次のサンプルコードでは、
action( )メソッドを実行した回数を出力しています。

結果は、参照を共有しない構造体は実行回数とcountに誤差が発生し
参照を共有するクラスは実行回数と同じcountを出力しました。

```swift

protocol Target {
    var type: String { get set }
    var count: Int { get set }
    mutating func action()
}

extension Target {
    mutating func action() {
        count += 1
        print("type: \(type), count: \(count)")
    }
}

// 構造体を定義(Targetに準拠している)
struct CountStruct: Target {
    var type: String = "Struct Type"
    var count: Int = 0
}

// クラスを定義(Targetに準拠している)
class CountClass: Target {
    var type: String = "Class Type"
    var count: Int = 0
}

struct Timer {
    // 制約(Targetプロトコルに準拠している型)
    var target: Target
    
    mutating func startTimer() {
        for _ in 0..<5 {
            target.action()
        }
    }
}

let countStruct = CountStruct()
var timer1 = Timer(target: countStruct)
timer1.startTimer()
print(countStruct.count)

print("----")

let countClass = CountClass()
var timer2 = Timer(target: countClass)
timer2.startTimer()
print(countClass.count)

実行結果
type: Struct Type, count: 1
type: Struct Type, count: 2
type: Struct Type, count: 3
type: Struct Type, count: 4
type: Struct Type, count: 5
0
----
type: Class Type, count: 1
type: Class Type, count: 2
type: Class Type, count: 3
type: Class Type, count: 4
type: Class Type, count: 5
5
```
**〜コード解説〜**

**プロトコル**
・`protocol Target { ・・・ }`
Targetプロトコルを定義しています。
このプロトコルに準拠する型は、`type`と`count`を定義しなければなりません。

・`extension Target { ・・・ }`
プロトコルエクステンションをしています。
Targetプロトコルに準拠している型は`mutating func action() { ・・・ }`を
使うことができるようになります。

action( )メソッドでは、
実行した回数を記録するためにcountプロパティの値を+1しています。

**型の定義**
・`struct CountStruct: Target { ・・・ }`
・`struct CountClass: Target { ・・・ }`
どちらもTargetプロトコルに準拠した型で
要求されているプロパティを実装しています。

・`struct Timer { ・・・ }`
型内に`var target: Target`を定義しており、
インスタンス化する際の引数にTargetプロトコルに準拠している型を指定しています。

`mutating func startTimer() { ・・・ }`を定義し、
targetプロパティに入っている型のaction( )メソッドを５回実行しています。

action( )メソッドは、プロトコルエクステンションで実装したメソッドで、
Targetプロトコルに準拠している型が使えるメソッドです。

**それ以降の処理**
・`let countStruct = CountStruct()`
定数countStructにCountStruct型をインスタンス化したものを代入しています。
しつこいですが、CountStruct型はTargetプロトコルに準拠しています。

・`var timer1 = Timer(target: countStruct)`
変数Timer1にTimer型をインスタンス化したものを代入しています。
引数にはTargetプロトコルに準拠している型を渡さないといけないので、
定数countStructを渡しています。

・`timer1.startTimer()`
Timer型に定義してあるstartTimer( )メソッドを実行しています。
メソッド内に記述されているfor-in文で５回action( )メソッドを実行しています。

・`print(countClass.count)`
action( )メソッドを実行するたびにcountプロパティの値をインクリメントしています。
なので、countプロパティにアクセスし値が何になっているか確認します。(想定は5回)

処理はこのような流れになります。
結果は、**構造体のcountは0でクラスのcountは5になりました。**

構造体の値が0の理由を説明します。
`let countStruct = CountStruct()`の箇所の段階で、
countStruct.countの値は0の状態です。

`var timer1 = Timer(target: countStruct)`で
引数にcountStructを渡していますが、
**このcountStructは定数countStructのコピーになります。**

つまり、定数countStructをcountStruct(A)とすると、
`Timer(target: countStruct)`のcountStructはcountStruct(B)となります。

startTimer( )メソッドで行っている`count += 1`という処理は、
countStruct(B)のcountにインクリメントを行っています。

`print(countStruct.count)`のcountStructはcountStruct(A)なので、
countの値は0の状態のままということです。

クラスの場合は、
`var timer2 = Timer(target: countClass)`の引数は
countClassのコピーではなく**参照先**なので同じ参照先のcountを更新することになります。

なので結果が5となります。

###インスタンスのライフサイクルに合わせて処理を実行する。

クラスにあって構造体にはない特徴の一つに、デイニシャライザがあります。

デイニシャライザはクラスのインスタンスが解放された時点で即座に実行されるため、
**インスタンスのライフサイクルに関連するリソースの解放操作を結びつけることができます。**

次のサンプルコードでは、
初期化時に変数dataに値を入れていますが、
変数sampleにnilを代入することでSample型のインスタンスが破棄され、
破棄されるタイミングでデイニシャライザを通じて変数dataをnilにしています。

```swift

var data: String?

class Sample {
    init() {
        print("Create a data")
        data = "a data"
    }
    
    deinit {
        print("Clean up the data")
        data = nil
    }
}

var sample: Sample? = Sample()
print(data ?? "nil")

sample = nil
print(data ?? "nil")

実行結果
Create a data
a data
Clean up the data
nil
```

構造体とクラスのどちらで設計をすればいいのかは、
これらの紹介で明確になったと思います！

基本は構造体を使用し、必要な時にのみクラスを使用する感じです。

構造体についての記事もありますので、
構造体について知りたい型はそちらをご覧ください。

以上、最後までご覧いただきありがとうございました。