なぜDelegateをプロパティに持つとweakを指定しなければいけないの？

#はじめに
strongやweakなど所有属性を適当につけると、
メモリリークを起こすと、先輩方によく突っ込まれませんか？
どういうことなのか、整理してみました。

よく言われるケースは、
OutletやDelegate、Closuresなどですかね？
SwiftLintでもワーニングが出ます。
今回は、Delegateを例に説明してみます。

## サンプルの説明
1st画面、2nd画面、3rd画面の3画面の構成とします。

### 1st画面の仕様
ボタンを押下すると、2nd画面へ遷移します。

```swift:FirstViewController.swift
import UIKit

final class FirstViewController: UIViewController {

    @IBAction func didTap2ndScene(_ sender: UIButton) {

        let secondVC = UIStoryboard.viewController(storyboardName: "Main",
                                             identifier: "SecondViewController")
        self.navigationController?.pushViewController(secondVC!, animated: true)

    }
}
```

### 2nd画面の仕様
3rd画面のインスタンスをプロパティで管理します。
2nd画面から3rd画面のDelegateプロパティに自分自身をセットします。

ボタンを押下すると、3rd画面へ遷移します。

```swift:SecondViewController.swift
import UIKit

final class SecondViewController: UIViewController {

    var thirdVC: ThirdViewController?

    @IBAction func didTap3rdScene(_ sender: UIButton) {

        thirdVC = UIStoryboard.viewController(storyboardName: "Main",
                                             identifier: "ThirdViewController")
        if let vc = thirdVC {
            vc.delegate = self
            self.navigationController?.pushViewController(vc, animated: true)
        }
    }
}

extension SecondViewController: ThirdDelegate {

    func completion() {
        print(#function)
    }
}
```

### 3rd画面の仕様
delegateをプロパティで管理します。（所有属性を省略し、strongとします。）

ボタンを押下すると、delegateで2nd画面へ通知し、画面を閉じます。

```swift:ThirdViewController.swift
import UIKit

protocol ThirdDelegate: class {
    func completion()
}

final class ThirdViewController: UIViewController {

    var delegate: ThirdDelegate?

    @IBAction func didTapGoBack(_ sender: UIButton) {

        delegate?.completion()
        self.navigationController?.popViewController(animated: true)
    }
}
```

## では、どのタイミングでメモリリークが起こるのでしょうか？
正解は、２nd画面を閉じたタイミングでメモリリークを起こします。

理由としては、2nd画面は、3rd画面のインスタンスをstrongで保持しています。
また、3rd画面は、delegateをstrongで保持しています。
つまり、循環参照となり、お互いのオブジェクトを参照している状態になっています。

<img width="746" alt="スクリーンショット 2017-04-17 17.57.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/71694/4a1c3d39-1d00-a7a6-ca20-6e22b683df4e.png">

これによりInstrumentsのLeak Checksでモニタリングしても、メモリリークを検出します。

![スクリーンショット 2017-04-17 17.48.58.png](https://qiita-image-store.s3.amazonaws.com/0/71694/289b84d0-28fc-b030-d010-7aaf1535504a.png)

## それでは、どうするか？
3rd画面で管理しているdelegateの所有属性をweakにします。
または、2nd画面で管理している3rd画面のインスタンスをweakにします。
これにより循環参照を防止します。

<img width="749" alt="スクリーンショット 2017-04-17 17.59.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/71694/f91adc98-e549-d8d7-6752-c7f6b3f2f564.png">

```swift:ThirdViewController.swift
import UIKit

protocol ThirdDelegate: class {
    func completion()
}

final class ThirdViewController: UIViewController {

    weak var delegate: ThirdDelegate?

    @IBAction func didTapGoBack(_ sender: UIButton) {

        delegate?.completion()
        self.navigationController?.popViewController(animated: true)
    }
}
```

確認したところ、InstrumentsのLeak Checksでモニタリングしても、メモリリークは発生しなくなりました。

![スクリーンショット 2017-04-17 17.50.47.png](https://qiita-image-store.s3.amazonaws.com/0/71694/2982b7e3-c17e-f031-d0d9-046ef73dbe92.png)

# まとめ
ARCってよくわからないなという方は、
InstrumentsのLeak Checksでモニタリングしてみると勉強になりそうですよ。

Closuresも考え方は同じです。
該当クラスからクロージャーを参照し、クロージャー内から該当クラスのプロパティを参照しているため、
循環参照が発生し、メモリリークを起こします。

しかしながら、可能であれば変数のスコープを狭くし、ローカル変数で管理すれば、
よりメモリリークを防ぐことができそうですね。