値型と参照型 どちらを採用すべきか


## 値型と参照型
どちらを選べばいいか、結論から言うと、
データを　**独立させたいのか**、 **参照・共有したいのか**　で決定すればいい。

ただ正直、基本的なことをするなら `class` でも `struct` でもいい気がしている。
たとえば、内部のプロパティは同じで `class`,`struct` 別で定義してみる。
```swift:
struct StructSample{
    var person:String
    var age:Int
}

class ClassSample{
    var person:String
    var age:Int
    
    init(person: String, age: Int) {
        self.person = person
        self.age = age
    }
}
```
その後、インスタンスを作成 + 値の変更を行うとする。

```swift:Structの結果
var structSample1 = StructSample(person: "Taro", age: 10)
var structSample2 = StructSample(person: "Jiro", age: 12)
structSample1.person = "Kyoko"
structSample2.person = "Hanako"

print(structSample1.person) // Kyoko
print(structSample2.person) // Hanako
```
```swift:Classの結果
var classSample1 = ClassSample(person: "Taro", age: 10)
var classSample2 = ClassSample(person: "Jiro", age: 12)
classSample1.person = "Kyoko"
classSample2.person = "Hanako"

print(classSample1.person) // Kyoko
print(classSample2.person) // Hanako
```

得られる結果は `struct`, `class` ともに同じである。
簡単なことをするなら、どちらでも同じ結果が出るため、あまり気を使って選定しなくていいが、より複雑なことを想定しているなら、意図して `struct`, `class` の選定が必要になってくる。

値型と参照型で違いが出てくるのは、以下のような場合である。

#### 値型と参照型の挙動の違い

```swift:struct
var structSample1 = StructSample(person: "Taro", age: 10)
var structSample2 = structSample1
structSample2.person = "Hanako"

print(structSample1.person) // Taro（影響なし）
```
`Struct` は値型である。これは、**コピー** である。
`var structSample2 = structSample1` とした時、`sample2` は `sample1` をコピー＆ペーストされた状態であり、`sample1` と `sample2` は独立している。 `sample2` を書き換えても、 `sample1` には影響しないし、逆の操作でも同じく影響しない。

つまり、両者は全くの別物で、それぞれの値を持つため **値型** とされており、結びつきもなく、お互い影響し合わない。
```swift:class
var classSample1 = ClassSample(person: "Taro", age: 10)
var classSample2 = classSample1
classSample2.person = "Hanako"

print(classSample1.person) // Hanako（影響される）
```
`Class` は参照型である。これはエイリアスである。
`var classSample2 = classSample1` とした時、`sample2` は `sample1` を参照した状態であり、 `sample1` と `sample2` は結びついている。

* `classSample1`：`ClassSample`クラスのインスタンス
* `classSample2`：インスタンスである`classSample1`を参照している。

英語の "alias" ＝「通称」「別名」と言う意味で、まさしく `sample2` は `sample1` の別名であり、これは `sample2` にアクセスされると、 `sample1` を参照することと同じである。

そのため、 `sample2` を変更とすると、 `sample1` インスタンスが変更されることになる。

## `struct` を採用すべき時
* 値をコピーして独立して使いたい場合

<details><summary>詳細</summary>

| ケース | 説明 |
|:-|:-|
| 独立したデータを持ちたいとき | ユーザー情報、位置情報、商品のデータなど |
| 変更が共有されると困るとき | 他の部分に影響を与えたくないとき |

</details>

たとえば、アカウントのように沢山のインスタンスを想定し、それぞれ独立した値を持たせたい場合は、
```swift:
struct User{
    var name:String
    var age:Int
}
```
のようにする。アカウントなどの個別で独立した情報は、値型である `struct` を用いるといい。

```swift:
var user1 = User(name: "Taro", age: 10)
var user2 = user1
user2.name = "Hanako"
print(user1.name) // Taro
print(user2.name) // Hanako
```
新たに `Hanako` さんのデータを作成したいときは、素直に `var user2 = User(name: "Hanako", age: 10)` とすればいいが、`var user2 = user1` とした場合でも、値がコピーされるだけなので、意図せず `user2`が変更されても、独立したデータ同士は影響を及ぼし合わないため、`user1` には影響しない。安全である。

## `class` を採用すべき時

* 状態を共有したい
* ライフサイクルや参照が重要
* 継承を使いたい

<details><summary>詳細</summary>

| ケース | 説明 |
|:-|:-|
| インスタンスの状態を共有したい | UIコンポーネント、データマネージャ、キャッシュなど |
| ライフサイクル管理が必要 | メモリ管理（ARC）やタイミングを制御したいとき |
| 継承が必要 | あるクラスを基にして派生クラスを作りたいとき |
| 他からアクセスされる状態を持ちたい	 | 監視されるオブジェクトなど（例：ObservableObject）|

</details>


アプリの状態を表す `UserManager` を想定してみた。現在ログイン中のアカウントを管理するなど、1つのアプリで1つだけ値を持つ場合などに参照型は有効である。
```swift:
class UserMangaer{
    static let shared = UserMangaer()
    var currentUser:String = "Taro"
    
    private init(){}
}
```
たとえばログイン中のユーザが２人いるというのはおかしい。そのため、このクラスはシングルトンで作成した。

<details><summary>シングルトンとは</summary>
1つのクラスに対して、アプリ内で1つしかインスタンスを作らせないようにする設計パターン。

* 現在ログイン中のユーザー情報
* 設定の状態
* ネットワークマネージャー
* サウンドコントローラー

など、これらは「アプリ全体でひとつだけ」でいい。むしろ複数インスタンス作られても困る。
そのような時に活用できる設計パターンが「シングルトン」である。

インスタンスの作成は`クラス名()`でできるが、複数のインスタンスを許可しないためにも、`init()`に制限をかける。
```swift:
class UserManager{
    private init () { }
}
```
`private` とすることで、`UserManager` クラス外では`init()`を使用できなくする。
次に、`UserManager` クラス中に 自身のインスタンスを一つ作成しておく。

```swift:
class UserManager {
    static let shared = UserManager()  // インスタンスは1つだけ。staticで定義
    private init() {}                 // 外からは初期化できないようにprivate化
}
```
`UserManager.shared`で、たった一つのインスタンス `shared` にアクセスできる。
```swift:
let sharedManager = UserManager.shared
```
このような設計にすることで、アプリ内であれば、どこからでも `shared` で呼べ、どこからアクセスしても同じ情報を取得でき・変更があればアプリ全体でその変更が反映されるようになる。

</details>

<details><summary> static キーワード</summary>

`static`キーワードを用いて`shared`を定義するが、`static`は「全インスタンスで共通の情報」を定義する際に便利である。また、`static`を用いてプロパティを定義すると、アクセス時、インスタンス化が不要になる。

```swift:
struct Roster{
    static var company = "Apple.inc"
    var name:String
}
```
社員個票用のクラスを作成した。
この時、`company`,`name`にアクセスしてみよう。
通常の定義をした`name`プロパティは、インスタンス化をしてから`.name`でアクセスする。
```swift:
let employee = Roster(name: "Sam")
let employeeName = employee.name
```
一方、`static`で定義した`company`プロパティはインスタンス化が不要である。
`company`プロパティは全インスタンスで共通の値であるため、インスタンスごとに用意するのではなく、クラス自体にただ一つ用意されたプロパティとして扱う。

アクセス時は`クラス名.プロパティ`でアクセスする。
```swift:
let companyName = Roster.company
```

#### staticはクラスにくっついてるって分かってるけど、共通であればインスタンスからアクセスできても自然では？

たとえば以下は許可されていない。
```swift:
let employee = Roster(name: "Sam")
let companyName = employee.company //インスタンスからアクセス->(エラー)
```
Samの例を見てみよう。

```swift:
let sam = Employee(name: "Sam")
print(sam.companyName) // ← 「Samの会社の名前」-> うーん (ちなみにこう書くとエラーが出る)
```
これは一見できるが、「会社はSamのもの」と勘違いされる可能性がある。
実際は、全社員で共有する会社名を表しているため、クラス名からアクセスするべき。(swiftらしい)

</details>


さらに参照型である `class` を使用しているため、
```swift:
var sharedManager = UserMangaer.shared
var anotherReference = UserMangaer.shared
```
としてインスタンスにアクセスしたとして、`anotheReference`　にのみ値の変更をかけてみることにする。
```swift:
anotherReference.currentUser = "Hanako"
```
くどいほど書いているが、`class` は参照型なので、`shared`　に変更を加えると、`sharedManager` にも変更がかかったように見える。かかったように見えるというのは、実際　`sharedManager`　を変更しているのではなく、`sharedManager`　は　`shared`　を参照しているから、変更された　`shared`　にアクセスしているだけである。
```swift:
print(sharedManager.currentUser) // Hanako
print(anotherReference.currentUser) // Hanako
```

## 安全に `class` を使う
`class` を利用する際の怖いポイントは「意図せず書き換えられてしまう」ところである。

もし、`struct`　で定義すべきアカウント情報を　`class`　で定義した場合、どのような危険性があるか。
```swift:
struct User{
    var name:String
    var age:Int
}
```
これを
```swift:
class User{
    var name:String
    var age:Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}
```
としてみる。
このとき、`struct`　の時と同じように値のコピーを試みたとする。
```swift:
var user3 = User(name: "Taro", age: 10)
var user4 = user3
user4.name = "Hanako"
```
出力結果は以下のようになる。
```swift:
print(user3.name) // Taroとなって欲しいのに、Hanako
print(user4.name) // Hanako
```
`UIKit`　をメインに扱う場合、`class`　が基本になっているため、何気に　`class` を使用してしまいがちだが、
何気に行った操作が、意図していない結果を生む場合がある。

### 工夫する点
#### 外部から直接プロパティ変更されたくない → private(set)
`class` に存在するプロパティで、
勝手に書き換えられると困るが、見られてもOKなプロパティは、
外部から読み取りを許可し、書き込みを禁止する`private(set)` を利用すると良い。
```swift:
class Counter {
    private(set) var count = 0

    func increment() {
        count += 1
    }
}
```
これを`Counter`クラスとは別の`Test`クラスを用意して、`count` プロパティへの読み込み・書き込みを試してみる。
```swift:
class Test{
    let counter = Counter()
    
    func countCheck(){
        print(counter.count) // 読み取りなのでOK
    }
    
    func changeCount(_ number:Int){
        counter.count = number // エラー：書き込みは禁止
    }
}
```
外部からは読み取りは可能なので、`countCheck()` は呼び出し可能だが。`changeCount()`は`private(set)`で定義した読み取り専用プロパティに書き込むことはできないので、エラーが発生する。
このように、不要な・意図しない書き込みを防ぐため、外部からは読み取り専用にしてしまうことができる。

#### 明示的にコピーしたいなら「手動コピー」する
`class`では `struct` のように値をコピーすることは容易でない。
直感的に書くと、以下のように
```swift:
var instance2 = instance1 
```
としてしまいがちだが、参照型ではコピーできないことを忘れてはいけない。
コピーを実現するには少し工夫が必要である。以下のように関数を用意することで実現可能である。
```swift:
class User {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func copy() -> User {
        return User(name: self.name, age: self.age)
    }
}
```
```swift:
let user = User(name:"Taro",age:12)
let copyUser = user.copy()
```

## まとめ
**独立させたいのか** , **参照・共有したいのか**に応じて、`static`,`class`の選定を適切に行いたい。