Swift の参照サイクル
開発者にとってメモリ管理は通常、簡単なことではありません。自動参照カウント(ARC)のおかげで、iOS 開発者にとってメモリ管理が簡単になります。ほとんどの場合、それだけで機能します。ただし、参照サイクルは非常に扱いが難しく、多くの経験豊富な開発者がそのような間違いを犯すのを見てきました。
公式ドキュメントでは主に 2 つのオブジェクト A<->B 間の基本的な参照サイクルについて説明しています。この記事で紹介するのは、3 つまたは 4 つのオブジェクト間の参照サイクルですが、これを見つけるのはさらに困難です。
メモリリークのあるアプリ
デモは 2 つの画面を持つ非常にシンプルな iOS プロジェクトです。最初の画面「InitialViewController」で、ユーザーはボタンをクリックして 2 番目の画面「SampleViewController」を起動し、閉じるボタンをクリックして 2 番目の画面を閉じることができます。2 番目の画面は、VIP クリーン アーキテクチャを使用してコーディングされています。
ユーザーがアプリを楽しんでいる一方で、メモリのリークが発生しています。以下のXcodeの「Debug Memory Graph」機能の画面では、「SampleViewController」、「SamplePresenter」、「SampleInteractor」、「SampleRouter」のインスタンスがそれぞれ5つずつリークしていることがわかります。メモリリークが多すぎると、アプリはシステムによって強制終了され、アプリ開発者にとっては非常に恥ずかしいことになります。
複数のオブジェクト内の参照サイクル
以下のソースコードを詳しく見て、メモリリークが発生する理由を調べてみましょう。
class SampleViewController: UIViewController, SampleViewControllerInterface {
var interactor: SampleInteractorInterface?
// omitted the rest for simplicity
}
class SampleInteractor: SampleInteractorInterface {
var presenter: SamplePresenterInterface?
// omitted the rest for simplicity
}
class SamplePresenter: SamplePresenterInterface {
var viewController: SampleViewControllerInterface?
var router: SampleRouterInterface?
// omitted the rest for simplicity
}
class SampleRouter: SampleRouterInterface {
var viewController: UIViewController?
// omitted the rest for simplicity
}
実際には 2 つの参照サイクルがあります。
- インタラクター -> プレゼンター -> ViewController -> インタラクター
- インタラクター -> プレゼンター -> ルーター -> ViewController -> インタラクター
これを解決するには、以下の変更のように弱い参照を使用できます。
次に、次の図に示すように、VIP には参照サイクルがなくなりました。
ブロックキャプチャに関する基準周期
ほー!2 つの参照サイクルを修正しただけなので、とても良い感じですが、まだ終わっていません。ここでアプリを実行すると、メモリ リークがまだ発生していることがわかります。原因はコードの以下の部分です。
class CustomButton: UIView {
var buttonClickHander: (() -> Void)?
// omitted the rest for simplicity
}
class SampleViewController: UIViewController, SampleViewControllerInterface {
private lazy var customButton: CustomButton = {
let button = CustomButton()
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private func setUpUI() {
view.addSubview(customButton)
customButton.buttonClickHander = {
self.interactor?.customButtonClicked()
}
// ...
}
}
問題の場所がわかれば、修正は簡単です。以下を参照してください。
まとめ
要約すると、複数のオブジェクト内で発生する 2 種類の参照サイクルについて説明しました。デモ プロジェクトはここで見つけることができます。修正は簡単にレビューできるようにプル リクエストとして用意されています。この記事が参照サイクルをより深く理解し、自信を持って参照サイクルを回避するのに役立つことを願っています。

![とにかく、リンクリストとは何ですか?[パート1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































