XcodeでiOSアプリのメモリリークを見つける方法
iOSアプリの動作が徐々に重くなったり、画面遷移を繰り返すうちに強制終了したりする場合、メモリリークが原因かもしれません。SwiftやSwiftUIでは参照カウント方式のARCがメモリを管理しますが、循環参照が残ると不要なオブジェクトを解放できなくなります。
Xcodeには、リークを検出するためのデバッグ機能が複数用意されています。特にMemory Graph DebuggerとInstrumentsのLeaksは、発生箇所の特定と継続的な計測に役立ちます。
画面の見た目だけでは判断しにくい問題だからこそ、再現手順を作り、使用量の変化を確認しながら調査することが大切です。
| ツール | 得意なこと | 利用するタイミング |
|---|---|---|
| Memory Graph Debugger | 参照関係や残存オブジェクトの確認 | 原因を素早く調べたいとき |
| Instruments - Leaks | リークやヒープ使用量の計測 | 長時間操作を検証するとき |
| Xcodeのデバッグゲージ | メモリ使用量の変化を確認 | 開発中の簡易チェック |
| deinit | オブジェクト解放の確認 | 特定クラスを追跡するとき |
メモリリークが起きる仕組み
Swiftのクラスは参照カウントで管理されます。インスタンスを参照する変数がなくなると解放されますが、2つのオブジェクトが互いに強参照すると、外部から使われていなくても参照カウントがゼロになりません。この状態が循環参照です。
典型例は、クロージャがselfを強く参照するケースです。タイマー、通知、非同期処理、デリゲートなども原因になりやすく、画面を閉じた後もViewControllerが残り続けることがあります。
XcodeのMemory Graph Debuggerを使う
アプリを実行して問題の画面を開き、画面遷移や戻る操作を行います。その後、デバッグエリアにあるメモリグラフのボタンを押すと、現在メモリ上に残っているインスタンスと参照関係を確認できます。
解放されるはずのViewControllerやViewModelが残っている場合は、該当オブジェクトを選択して参照元をたどります。紫色の警告表示が出ているオブジェクトは、Xcodeがリークの可能性を検出した対象です。
InstrumentsのLeaksで計測する
XcodeのProductメニューからProfileを選び、Instrumentsを起動します。テンプレートからLeaksを選択し、アプリを操作しながらメモリ使用量とリークの検出結果を記録します。
一度画面を開いて閉じるだけではなく、同じ操作を何度も繰り返すことが重要です。リークがある場合、メモリ使用量が階段状に増え、画面を離れても完全には戻らない傾向が見られます。
循環参照をコードから見つける
クロージャ内でselfを使う場合は、処理の寿命に応じてweak selfの扱いを検討します。たとえば非同期処理が長時間続く場合、[weak self]を使うことでクロージャがViewControllerを保持し続けるリスクを下げられます。
ただし、すべてのクロージャにweakを付ければよいわけではありません。処理中にselfが必ず必要な場合や、短時間で終了する処理では、弱参照によって意図せず処理が中断される可能性があります。
SwiftUIで注意したい保持関係
SwiftUIではView自体が構造体であるため、UIKitと同じ感覚でリークを判断できません。@StateObject、@ObservedObject、@EnvironmentObjectの所有関係を整理し、生成と破棄の責任を適切な場所に置く必要があります。
ObservableObjectがタイマーや通知を保持している場合は、購読解除やタイマー停止も確認します。SwiftUIのリストをカスタマイズする実装では、リストビューの作り方も参考にしながら、行ごとに不要なオブジェクトを生成していないか確認するとよいでしょう。
再現手順とログを組み合わせる
リーク調査では、問題が発生する操作を具体的に記録します。「一覧を開く」「詳細へ移動する」「戻る」「検索を実行する」といった手順を固定すると、修正前後の比較が容易になります。開発イベントや勉強会で得た知見を実装に反映する場合も、活動の背景と同じように、経緯をメモしておくと後から検証しやすくなります。
特定のクラスにdeinitを追加して、画面を閉じたタイミングでログが出るか確認する方法も有効です。ログが出ない場合は、Memory Graph Debuggerで残っている参照を調べ、通知、タイマー、クロージャ、デリゲートの順に確認します。
開発中に続けやすい予防策
メモリリークはリリース直前にまとめて探すより、機能を追加するたびに小さく確認する方が発見しやすくなります。コードレビューでは、非同期処理と購読処理に強い参照が残っていないかを確認します。
日常的なチェックには、次の方法が向いています。
- 画面の開閉を複数回繰り返してメモリ使用量を見る
- 重要なViewModelやViewControllerに
deinitを一時的に追加する - タイマー、通知、Combine購読の終了処理を確認する
- 長時間操作をInstrumentsで計測する
- 修正前後のMemory Graphを比較する
Xcodeのデバッグ機能を使えば、感覚だけに頼らず、オブジェクトの残存と参照関係を視覚的に追跡できます。気になる画面から操作手順を固定し、Memory Graph DebuggerとInstrumentsを使い分けて、安定したiOSアプリの実装につなげていきましょう。