【Swift】Memory Managementについて

開発業務等において`weak self`というコードが頻繁に現れ、「weak? なんだこれは?」となった自分のような方に向けて、SwiftのMemory Managementを解説したいと思います。
## メモリマネジメントとは？
SwiftにはARC(Automatic Reference Counting)と呼ばれる、参照されなくなった(つまり使われなくなった)変数を自動でメモリから解放する仕組みが備わっています。例えば、以下のようなコードを考えます。
```swift
class Book {
    let name: String
    let author: Author

    init(name: String, author: Author) {
        self.name = name
        self.author = author
    }

    deinit {
        print("Book \(name) deinit")
    }
}

class Author {
    let name: String

    init(name: String) {
        self.name = name
    }

    deinit {
        print("Author \(name) deinit")
    }
}

do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    print(book) // Book
    // Book toy story deinit
    // Author Bob deinit
}
```
このような場合、`do`で囲んだスコープ内変数は処理が終わった途端に使用されなくなるため、`print(book)`が終了した途端にどちらも`deinit`されていることがわかります。
`book`で参照していた`author`は`book`からの参照が無くなったことでどこからも利用されなくなり、同様に`deinit`されていることは意識しておいてください。
ちなみに、`deinit`の順番は参照の作成順と正反対の順番になります。

しかし、以下のような場合はどうでしょうか。
```swift
class Book {
    let name: String
    let author: Author

    init(name: String, author: Author) {
        self.name = name
        self.author = author
    }

    deinit {
        print("Book \(name) deinit")
    }
}

class Author {
    let name: String
    // 追加
    var books: [Book] = []

    init(name: String) {
        self.name = name
    }

    deinit {
        print("Author \(name) deinit")
    }
}


do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    author.books.append(book)
    print(author) // Author
}
```
さっきまでの通り、`book`は`author`を参照していますが、同時に`author`も`books`プロパティの中身として`book`を参照しています。
この場合、`do`のスコープ内処理が終了しても互いの参照は残っているので、**deinitが呼ばれません。**
`deinit`されない変数はメモリから解放されずに、ずっとメモリ内に残り続けます。これをメモリリークと言います。
## メモリリークの解決策
### その１. weakを使用する
コードを以下のように書き換えます。
```swift
class Book {
    let name: String
    // 変更
    weak var author: Author?

    init(name: String, author: Author) {
        self.name = name
        self.author = author
    }

    deinit {
        print("Book \(name) deinit")
    }
}

class Author {
    let name: String
    var books: [Book] = []

    init(name: String) {
        self.name = name
    }

    deinit {
        print("Author \(name) deinit")
    }
}

do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    author.books.append(book)
    print(author) // Author
    // Author Bob deinit
    // Book toy story deinit
}
```
`weak`がついていると、変数が使われなくなった場合に自動で検出し、変数を`nil`に変換してくれます。
そのため、今回は`author`がprintされた後使われなくなり、`book`からの参照が外れて`deinit`することができたことで、`author`の参照から外れた`book`が続けて`deinit`することができます。

### その2. unownedを使用する
もう一つ、`unowned`プロパティを用いることによってもメモリリークを回避できます。
```swift
class Book {
    let name: String
    // 変更
    unowned let author: Author

    init(name: String, author: Author) {
        self.name = name
        self.author = author
    }

    deinit {
        print("Book \(name) deinit")
    }
}

class Author {
    let name: String
    var books: [Book] = []

    init(name: String) {
        self.name = name
    }

    deinit {
        print("Author \(name) deinit")
    }
}

do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    author.books.append(book)
    print(author) // Author
    // Author Bob deinit
    // Book toy story deinit
}
```
`weak`との大きな違いは、`Optional`型かそうでないかです。そのため、unwrapせずに変数を用いたい場合(letで宣言したい場合)に用いることができます。
`unowned`は、付与したプロパティで参照をしても、参照カウントには影響しないようにする修飾子です。そのため、`weak`同様、`author`は使われなくなると`deinit`することができます。

ただし、注意点としてはこちらがforce-unwrapであるという点です。つまり、`author`が必ず存在すると保証できる場合のみ、使用をするべきものです。
`author`がない状態で`book`を参照しようとするとクラッシュします。そういった点では、`weak`の方が安全と言えそうです。

## Closureにおけるメモリマネジメント
例えば、以下のようにclosureをクラス内に定義する場合を考えます。
```swift
class Book {
    let name: String
    unowned let author: Author
    // 追加
    lazy var warning: () -> String = {
        return "please contact \(self.author.name) if you're trouble with understanding \(self.name)"
    }
    ...
}

do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    print(book.warning()) // please contact Bob if you're trouble with understanding toy story
    // Author Bob deinit
}
```
このようにclosureをクラス内に定義する場合には、クラス内の定数を使って何かを行うことが多いかと思いますが、実行してもらうとわかる通り、**Bookのdeinitがいつまでも呼ばれません。**
closure内でBook自身への参照を保持してしまっているためです。
このようにclosureはselfの循環参照を起こしやすいため、適切にメモリマネジメントをする必要があります。
### Captured Listを使う
Swiftには、Captured Listと呼ばれる、Closureの中で使用する変数を再定義できる仕組みが存在します。
例えば、以下のようなコードを考えます。
```swift
var counter = 0
var closure = {
    print(counter)
}
var closure2 = { [counter] in
    print(counter)
}
counter += 1
closure() // 0
closure2() // 1
```
`closure()`は`counter`への参照を直接持っていますが、`closure2`はクロージャが宣言された際の`counter`の値を新たな`counter`という変数として再定義しています。(counter = counterとして再度定義されている)
そのため、`closure()`、`closure2()`の定義後に起こった変化に対し、前者は影響を受けていますが、後者は影響を受けません。

これは変数だけでなく、クラスオブジェクトへの参照にも使用できますが、その場合は少し勝手が変わります。
```swift
// nameをvar name: Stringに変更後
var book2 = Book(name: "toy story",
                 author: author)
var closure = {
    print(book2.name)
}
var closure2 = { [book2] in
    print(book2.name)
}
book2.name = "monsters inc"
closure()
closure2()
// monsters inc
// monsters inc
```
上記のように、どちらもclosure定義後の値が出力されているのがわかります。このように、captured listに登録した値がクラスオブジェクトの場合には、オブジェクトそのものへの参照を新たな変数として再定義していることがわかります。
そのため、クラスオブジェクトにはメリットがないように感じますが、実はMemory managementでこのcaptured listが大活躍します。

### closureにおける循環参照の防ぎ方
こちらも同様に、[メモリリークの解決策](#メモリリークの解決策)をそのまま使えます。`Book`クラスを以下のように書き換えてみましょう。
```swift
class Book {
    var name: String
    unowned let author: Author
    // 変更
    lazy var warning: () -> String = { [unowned self] in
        return "please contact \(self.author.name) if you're trouble with understanding \(self.name)"
    }
}

do {
    let author = Author(name: "Bob")
    let book = Book(name: "toy story",
                    author: author)
    print(book.warning()) // please contact Bob if you're trouble with understanding toy story
    // Book toy story deinit
    // Author Bob deinit 
}
```
`self`としてキャプチャした自身を`unowned`な定数とすることで、使用されなくなったら自動で`self`が解放され、無事に`deinit`されていることがわかります。
しかし先述の通り、`unowned`は安全な方法ではないので、ほとんどの場合、`[weak self]`が使われると思います。
```swift
class Book {
    var name: String
    unowned let author: Author
    // 変更
    lazy var warning: () -> String = { [weak self] in
        guard let self else { return "" }
        return "please contact \(self.author.name) if you're trouble with understanding \(self.name)"
    }
}
```
こうすることで、万が一`self`が存在しなくてもクラッシュせずにメモリマネジメントを行うことができます。
しかしこちらも先述の通りですが、`self`はオプショナル型であるため、アンラップしてから用いる必要があります。
例では`guard`構文でアンラップしていますが、`self?.author.name`のようにオプショナルチェインを利用する方法も考えられると思います。

## まとめ
- 参照がなくなった変数やクラスは、SwiftのARCと呼ばれる仕組みによってメモリから自動で解放される。
- 循環参照を防ぎ、メモリマネジメントをする方法は大きく分けて二つ
  - `unowned`: unwrapする必要がなく、nilにならないことが保証されている定数に用いる。
  - `weak`: Optional型としてunwrapする必要があるが、安全に循環参照を防ぐ際に用いる。
- Closureの中でクラス自身のプロパティを用いるときは、必ず`self`を付与する必要がある。その際、Captured Listでselfをキャプチャし、`weak`や`unowned`を用いることで自身との循環参照を防ぐことができる。

### 最後に
こちらは私が書籍で学んだ学習内容をアウトプットしたものです。
わかりにくい点、間違っている点等ございましたら是非ご指摘お願いいたします。