【プログラミング初心者】Swift基礎~構造体・クラス~

# 型を定義する
[以前](https://qiita.com/euJcIKfcqwnzDui/items/5d4c674a975c90a3db3f#%E5%9E%8B%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)Swiftにおける基本となる型について説明しました。
この基本型とif文とfor文さえあればプログラミングはできるのですが、これだけだとプログラムは煩雑になり管理しづらくなります。
そこである１つのデータや処理のまとまりを新しい型として定義することで全体の見通しをよくすることができます。
その型定義の方法に**構造体**や**クラス**という手法があります。

## 構造体
### 構造体の定義
構造体を定義するための構文は以下となります。

```swift
struct 型名 {
   ...
}
```

もう少し具体的な実装をします。会社の構造を例にしてみましょう。
会社を表す型として`Company`という構造体を作成します。
`Company.swift`というファイルを作成し、構造体を定義します。

```swift:Company.swift
struct Company {
}
```

これで`Company`という構造体が定義されました。
実際に`ViewController.swift`などから使ってあげましょう。
`Int`の実態は数値なので変数には数値を代入すればよかったのですが、構造体の実態は**オブジェクト**と呼ばれるデータのまとまりになります。
そのため`Int`とは少し違い作成する構文は`構造体名()`でオブジェクトを作成します。

```swift:ViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    var company: Company = Company()
}
```

### メンバ変数
さらにここから`Company`を表す属性を定義していきます。
会社を表すためにはどんな項目が必要でしょうか？
まず会社名、社長、あとは売り上げあたりを定義してみましょう。

```swift:Company.swift
struct Company {
    // 会社名
    var name: String
    // 社長
    var president: String
    // 売り上げ
    var earnings: Int
}
```

これで`Company`型はname, president, earningsという会社を表す属性を持つことになります。
これらの構造体を表すために定義した変数のことをCompanyの**メンバ変数**、もしくは単に**メンバ**と呼びます。

次に`ViewController.swift`をもう一度見てみましょう。
初期化の行で`Missing arguments for parameters 'name', 'president', 'earnings' in call`というエラーが発生しています。
「Companyに必要なメンバの実態がないので教えてください」というようなエラーです。
メンバを初期化時に入れてあげましょう。
エラーの左にある赤い[◎]をクリックし[Fix]を選択してあげると初期化のテンプレートをXcodeが作成してくれます。
メンバがある場合の初期化の方法は`構造体(メンバ１: 値, メンバ2: 値, ...)`です。

```swift:ViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    var company: Company = Company(name: "株式会社山田商事",
                                   president: "山田太郎",
                                   earnings: 1000000)
}
```

さてこれで「株式会社山田商事」という会社が作られました。
メンバ変数を参照・更新してみましょう。
メンバへアクセスする構文は`変数.メンバ`です。

```swift:ViewController.swift
var company: Company = Company(name: "株式会社山田商事",
                               president: "山田太郎",
                               earnings: 1)
print(company.name)            // "株式会社山田商事"
print(company.president)       // "山田太郎"
print(company.corporateNumber) // 1000000

company.name = "山田次郎"
print(company.president) // "山田次郎"
```

またメンバ変数はさらに別の構造体でも問題ありません。

会社のすぐ下には部署がぶら下がっていると仮定し、`Department`という構造体を`Department.swift`に定義します。
部署はとりあえず部署名と部長がいるだけにしましょうか。

会社の部署は複数の可能性があるので`Department`の配列で定義します。

```swift:Department.swift
struct Department {
    // 部署名
    var name: String
    // 部長
    var manager: String
}
```
```swift:Company.swift
struct Company {
    // 会社名
    var name: String
    // 社長
    var president: String
    // 法人番号
    var corporateNumber: Int
    // 部署
    var departments: [Department]
}
```
```swift:ViewController.swift
// 部署の作成
let developDepartment: Department = Department(name: "開発部",
                                               manager: "開発太郎")
let salesDepartment: Department = Department(name: "営業部",
                                             manager: "営業太郎")
// 会社の作成
var company: Company = Company(name: "株式会社山田商事",
                               president: "山田太郎",
                               earnings: 1000000,
                               departments: [developDepartment, salesDepartment])
```
部署へのアクセスはメンバのメンバにアクセスするように`.`を繋げていきます。

```swift:ViewController.swift
// 登録した部署にアクセス
print(company.departments[0].name) // "開発部"
```

### メソッド
構造体には振る舞い、つまり構造体が行う処理を定義することができます。
この振る舞いを**メソッド**と呼びます。メソッド定義の構文は以下となります。
(正確ではありませんが**関数**と呼ぶこともあります)

```swift
func メソッド名(引数) {
    ...
}
```
メソッドには**引数**と呼ばれるパラメータを与えることができます。
引数は`引数名: 型`というように`(arg1: String, arg2: Int, ...)`と定義します。

会社の振る舞いを考えてみましょう。
まず会社は売り上げをあげていくものなので、月の売り上げを加算するメソッドを定義します。

```swift:Company.swift
struct Company {
    // 会社名
    var name: String
    // 社長
    var president: String
    // 売り上げ
    var earnings: Int
    // 部署
    var departments: [Department]
    
    /// 売り上げを計上する
    /// - Parameter earnings: 月売り上げ
    mutating func sumUp(monthlyEarnings: Int) {
        self.earnings = self.earnings + monthlyEarnings
    }
}
```
少し注意が必要なのがfuncの前に`mutating`というものがあります。
これは構造体特有のものですが、今回`sumUp`の中で`earnings`というメンバ変数を更新しています。
このようなメンバ変数を更新するメソッドを定義する場合は頭に`mutating`をつけます。
`mutating`がついたメソッドは構造体が変数、つまり`var`を使って宣言されていなければならないというルールがあります。
[配列](https://qiita.com/euJcIKfcqwnzDui/items/5d4c674a975c90a3db3f#%E9%85%8D%E5%88%97)の`append`や`remove`と同じです。
少し脱線しますが実は配列も構造体として定義されています。
`append`などの定義を見ると`mutating`がついていることを確認できます。
配列構造体は配列自体はメンバ変数として保持しています。そのため`append`を呼び出すとメンバ変数が更新されるため`mutating`として定義されているというわけです。

さてそれでは定義したメソッドを呼び出してみましょう。

```swift:ViewController.swift
var company: Company = Company(name: "株式会社山田商事",
                               president: "山田太郎",
                               earnings: 1000000,
                               departments: [])

print(company.earnings) // 1000000

company.sumUp(monthlyEarnings: 1000000)
print(company.earnings) // 2000000

company.sumUp(monthlyEarnings: 200000)
print(company.earnings) // 2200000
```

これでメソッドを呼び出し値を更新できました。

### メソッド化する意味
メソッド呼び出したことで`company`の状態を更新できましたが、そんなことをしなくても更新できるのでは？と思う人もいるかもしれません。
そうです。先程紹介したメンバ変数にアクセスして更新する方法でも当然更新することができます。

```swift:ViewController.swift
print(company.earnings) // 1000000

company.earnings = company.earnings + 1000000
print(company.earnings) // 2000000

company.earnings = company.earnings + 200000
print(company.earnings) // 2200000
```

ではメソッド化することでどんなメリットがあるのでしょうか？

まず１つ目は処理に名前を付けられるという点があります。
メソッドとして定義することで`sumUp(monthlyEarnings: Int)`という処理ができ、メソッド名自体がどんな処理をするのか想像することができます。
これは変数に関しても同様のことが言えます。

２つ目は処理をまとめられるという点です。
先程のコードを見てください。簡単な処理ではありますが`print(company.earnings)`が何度も出てきます。
このような共通した処理が複数行に渡るとコード自体がかなり冗長になります。
メソッド化しておくと一度書いておくだけで何度も呼び出せます。

```swift:Company.swift
/// 売り上げを計上する
/// - Parameter earnings: 月売り上げ
mutating func sumUp(monthlyEarnings: Int) {
    print("before: \(self.earnings)")
    self.earnings = self.earnings + monthlyEarnings
    print("after: \(self.earnings)")
    print("----------")
}
```
```swift:ViewController.swift
var company: Company = Company(name: "株式会社山田商事",
                               president: "山田太郎",
                               earnings: 1000000,
                               departments: [])

company.sumUp(monthlyEarnings: 1000000)
company.sumUp(monthlyEarnings: 200000)
```
```swift:実行結果
before: 1000000
after: 2000000
----------
before: 2000000
after: 2200000
----------
```

3つ目はコードの変更に強くなるということです。
結局は２つ目のメリットと同じようなものですが、例えば今は単純に売り上げを足しているところを消費税も合計に入れたいという要件が出たとします。
複数箇所で計算していた場合全ての処理を修正しなければいけません。
これをメソッドを呼び出して処理をしておくと`sumUp`の中身だけを修正すればよく、呼び出し元の`ViewController.swift`には全く触れる必要がありません。

```swift:Company.swift
mutating func sumUp(monthlyEarnings: Int) {
    print("before: \(self.earnings)")
    // ここだけを修正する
    let tax = Int(Double(monthlyEarnings) * 1.1)
    self.earnings = self.earnings + monthlyEarnings + tax
    print("after: \(self.earnings)")
    print("----------")
}

```
```swift:ViewController.swift
var company: Company = Company(name: "株式会社山田商事",
                               president: "山田太郎",
                               earnings: 1000000,
                               departments: [])

company.sumUp(monthlyEarnings: 1000000)
company.sumUp(monthlyEarnings: 200000)
```
```swift:実行結果
before: 1000000
after: 3100000
----------
before: 3100000
after: 3520000
----------
```

このように構造体に処理を持たせておくことで、コードの可読性が上がりさらに変更に強いコードにすることができるのです。

## クラス
クラスも構造体と似たようなものでメンバ変数とメソッドを持つデータ構造を表します。
違いは後で次項で説明するので本項では定義の仕方について説明します。

### クラスの定義
クラスを定義するための構文は以下となります。基本的には構造体と同じです。

```swift
class クラス名 {
    ...
}
```

今度は人間クラスというものを定義し、オブジェクトを作ってみましょう。
作り方も構造体と同じです。

```swift:Human.swift
class Human {
    
}
```
```swift:ViewController.swift
var human = Human()
```

これで人間のオブジェクトを作成することができました。

クラスから作成されたオブジェクトのことは特に**インスタンス**と呼びます。
ですが経験上あまり明確に区別して話す機会はないように思うので基本的にはオブジェクトと言っておけば問題ないでしょう。

一応オブジェクト、インスタンスそれぞれの意味は

- オブジェクト
    - プログラム上で扱うデータ全般。その実体化したデータを１つのモノとして見る考え方。
- インスタンス
    - 何らかのクラスに基づいて実体化されたオブジェクト

となりますのでインスタンスもつまりはオブジェクトの一種です。
なのでインスタンスをオブジェクトと言っても誤りではないというわけです。

### プロパティ/メソッド
クラスも構造体と同じようにメンバ変数とメソッドを持たせることができます。
厳密には違いますがクラスで定義したメンバ変数を**プロパティ**と呼ぶととりあえずは覚えてください。

定義の仕方も同様です。
まずはプロパティを定義します。

```swift:Human.swift
class Human {
    // 名前
    var name: String
    //  年齢
    var age: Int
}
```
すると以下のようなエラーが発生します。

```
Class 'Human' has no initializers
```
「イニシャライザがありません」というようなエラーです。
**イニシャライザ**とは初期化の際に使用する、少し特殊なメソッドです
別の言語では**コンストラクタ**といい、つまりクラスを構成する人というイメージです。

構造体のときはこのエラーは発生しませんでしたが実は構造体は暗黙的にイニシャライザを自動で持ってくれます。
ですがクラスの場合は明示的に作ってあげる必要があります。

イニシャライザの定義の仕方は色々ありますが今回は最も単純なイニシャライザを定義します。
基本構文は`init(引数)`です。

```swift:Human.swift
class Human {
    // 名前
    var name: String
    //  年齢
    var age: Int
    
    /// 初期化
    /// - Parameters:
    ///   - name: 初期値となる名前
    ///   - age: 初期値となる年齢
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}
```

呼び出し方は`クラス名(引数)`です。
あまりやりませんが`クラス名.init(引数)`でも呼び出せます。

```swift:ViewController.swift
var human = Human(name: "山田太郎", age: 25)
var humanInit = Human.init(name: "山田太郎", age: 25)
```

構造体のときと同じ生成の仕方ができました。
クラスも構造体もイニシャライザのみ`クラス名(引数)`という呼び出し方ができます。

メソッドも構造体同様に定義でき、呼び出し方も同様です。

```swift:Human.swift
class Human {
    // 名前
    var name: String
    //  年齢
    var age: Int
    
    /// 初期化
    /// - Parameters:
    ///   - name: 初期値となる名前
    ///   - age: 初期値となる年齢
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    /// 話す
    func talk() {
        print("私の名前は\(self.name)です。")
    }
}
```
```swift:ViewController.swift
var human = Human(name: "山田太郎", age: 25)
human.talk()
```
```実行結果
私の名前は山田太郎です。
```

一点異なることはメソッド内でメンバ変数が更新される場合でも`mutating`は不要となります。
ここは構造体と異なる点です。

```swift:Human.swift
/// 年齢を更新する
/// - Parameter age: 年齢
func update(age: Int) {
    self.age = age
}
```

このようにしてアプリで扱うデータをオブジェクトという人間がわかりやすい単位で管理していきます。

# 最後に
今回は新しく自分で型を定義するために構造体とクラスについて説明しました。
このようにオブジェクトベースで考えていく方法を**オブジェクト指向プログラミング**といいます。
どんなオブジェクトを作れば実装しやすいか、管理しやすいかを考えながら実装してみてください。

また構造体、クラスどちらも似たようなものですがそれぞれ**値型**と**参照型**と言われメモリの確保のされ方が異なります。
そのあたりはまた別途記事にしていきます。

本記事とは別でプログラミング未経験からiOSアプリ開発が行えるようになることを目的とした記事を連載しています。
連載は以下にまとめていますのでそちらも是非もご覧ください。
http://naoyalog.com/