iOSアプリのライフサイクルと状態遷移
アプリが落ちたと思ったら実は凍結中だった、を見分けられる。5状態の遷移条件と状態保存・復元の仕組みを原理から押さえられる。
- 1.iOSアプリはNot Running/Inactive/Active/Background/Suspendedの5状態を遷移し、遷移のたびにUIApplicationDelegateのコールバックが呼ばれる。
- 2.Backgroundは時間制限つきの実行猶予、Suspendedはメモリに残るがCPUを一切使わない凍結状態で、両者は明確に区別される。
- 3.メモリ逼迫時にSuspended中のプロセスは通知なしに終了させられるが、State Restorationの仕組みで次回起動時にUIと状態を復元できる。
「バックグラウンドで動いている」の実態を分解する
iOSアプリを閉じてもホーム画面のアイコンからすぐ元の画面に戻れる——この体験の裏側には、単純な「起動中/終了」の二値ではなく、5段階の状態機械が存在します。OSはメモリと電力を守るため、ユーザーの目に触れない場所でアプリを段階的に凍結し、必要なら通知すらせずに終了させます。この設計を正確に理解しないと、「バックグラウンドで処理が止まる」「データが消える」といった不具合の原因を切り分けられません。ここでは各状態の定義、遷移を引き起こすイベント、そして状態保存・復元の内部動作を見ていきます。
5つの状態とその定義
iOSアプリのライフサイクルはUIApplication.Stateとして公開される値を軸に、実際には次の5段階で管理されます。
| 状態 | 定義 | CPU/メモリの扱い |
|---|---|---|
| Not Running | 起動していない、またはOSに一度も起動されていない | プロセスが存在しない |
| Inactive | フォアグラウンドにいるがイベントを受け取っていない過渡状態 | 実行中。UIは表示されるが入力処理は停止気味 |
| Active | フォアグラウンドで通常実行中。ユーザー操作を受け付ける | 実行中。CPU/メモリとも通常利用 |
| Background | 画面には出ていないがコード実行中 | 実行中だが時間制限つき。バックグラウンドタスクとして猶予される |
| Suspended | メモリ上にプロセスは残るがコードは一切実行されない | 凍結。CPUを使わずメモリのみ保持 |
ここで押さえるべきはBackgroundとSuspendedの違いです。Backgroundは「まだコードを実行できる」状態で、SuspendedはOSがプロセスをメモリスナップショットとして保持するだけの凍結状態です。Suspended中のアプリはタイマーもネットワーク通信も一切進行しません。
Inactiveは通過点に見えて重要です。着信、通知センターを引き下げた瞬間、コントロールセンター表示中など、画面には見えていてもユーザー操作が別のUIに奪われている間、アプリはInactiveに置かれます。Activeへ完全移行する前にワンクッション置くことで、入力の奪い合いによる誤動作を防ぐ設計です。
遷移を引き起こすイベントとコールバック
各遷移ではUIApplicationDelegate(SwiftUIではScenePhase)のメソッドが呼ばれ、アプリはここで保存・停止・再開の処理を行います。
| 遷移 | 引き金となるイベント | 対応するデリゲートメソッド |
|---|---|---|
| Not Running → Active | ユーザーがアイコンをタップして起動 | application(_:didFinishLaunchingWithOptions:) |
| Active → Inactive | 着信・通知センターやコントロールセンターの表示・ホームボタン長押し | applicationWillResignActive(_:) |
| Inactive → Active | 割り込みの解消・アプリへの復帰 | applicationDidBecomeActive(_:) |
| Inactive → Background | ホーム画面へ戻る・他アプリへ切り替え | applicationDidEnterBackground(_:) |
| Background → Inactive → Active | アプリへの再切り替え | applicationWillEnterForeground(_:) |
| Background/Suspended → Not Running | OSによる強制終了・端末再起動・ユーザーによるスワイプ終了 | 呼ばれない(通知なし) |
重要なのは最後の行です。SuspendedからNot Runningへの遷移は、アプリ側に何のコールバックも呼ばれず起こります。凍結中のプロセスはコードを実行していないため、終了を知らせようがないのです。したがって「終了時に必ず後始末をしたい処理」は、終了イベントを待つのではなく、Backgroundへ入るapplicationDidEnterBackgroundの時点までに完了させておく必要があります。
Background実行の時間制限
Inactiveを経てBackgroundへ入った直後、アプリは無制限に走り続けられるわけではありません。OSはbeginBackgroundTask(expirationHandler:)で確保する猶予期間を与え、これを使い切ると強制的にSuspendedへ落とします。
var bgTask: UIBackgroundTaskIdentifier = .invalid
func applicationDidEnterBackground(_ application: UIApplication) {
bgTask = application.beginBackgroundTask(withName: "finishUpload") {
// 猶予期限が来た場合の後始末。ここで必ずendBackgroundTaskを呼ぶ
application.endBackgroundTask(self.bgTask)
self.bgTask = .invalid
}
DispatchQueue.global().async {
self.uploadRemainingData()
application.endBackgroundTask(self.bgTask) // 完了したら早めに解放
self.bgTask = .invalid
}
}
猶予期間を使い切ってendBackgroundTaskを呼ばないままだと、OSはアプリを強制終了させます。これは通常の状態遷移ではなくペナルティであり、次回起動時にOSがクラッシュ扱いで記録することもあります。位置情報の継続取得やVoIP着信待受などの特定用途では、Info.plistにバックグラウンドモードを宣言することで、この時間制限を実質的に免除される仕組みが用意されていますが、これは例外的な許可であり原則ではありません。
メモリ逼迫時の強制終了と再起動
Suspended状態のプロセスはCPUを使いませんが、メモリは占有し続けます。端末のメモリが逼迫すると、OSはSuspended中のプロセスをメモリ解放の対象として選び、通知なしに終了させます。優先順位はおおむね「フォアグラウンドで動いているアプリ」を最後まで残し、Suspendedのアプリのうち最も長く使われていないものから終了させる方式です。
この終了はNot Runningへの遷移であり、ユーザーがアプリスイッチャーでアイコンを見ている限り、次にタップしたときはOSが新規プロセスとして再起動し、あたかも継続していたかのようにUIを復元します。ユーザーからはSuspendedと強制終了後の再起動の区別がつきません。この体験を成立させているのがState Restoration(状態保存・復元)です。
メモリ逼迫によるSuspended中の終了は正常なライフサイクルの一部であり、クラッシュログには通常記録されません。開発者がXcodeのデバッガでBackground/Suspended中のプロセスを観察していても、この終了はOSの通常のメモリ管理として静かに起こるため、明示的にシミュレートする手段(Xcodeの「Simulate Memory Warning」やデバイス設定)を使わないと検証できません。
状態保存・復元の内部動作
State Restorationは大きく2段階で構成されます。
| 段階 | タイミング | 保存・復元する内容 |
|---|---|---|
| 状態の保存(Encode) | Backgroundへ入る際、UIKitがビューコントローラ階層を走査 | 画面構成・ナビゲーションスタック・各ビューが指定したrestorationIdentifier単位の状態 |
| 状態の復元(Decode) | 強制終了後の再起動時、didFinishLaunchingWithOptionsより後 | 保存済みスナップショットからビュー階層を再構築し、アプリ固有データをコールバックで復元 |
UIKitはrestorationIdentifierが設定されたビューコントローラだけを保存対象とし、識別子のないビューは復元対象から外れます。復元時はまずストーリーボードやコード上の初期化でビュー階層を再生成し、そのうえで保存済みのスナップショットを流し込む2段構えです。これにより、単なる「最後の画面のスクリーンショットを見せる」演出ではなく、スクロール位置やテキスト入力途中の内容まで復元できる設計になっています。SwiftUIでは@SceneStorageがこの仕組みを簡潔なプロパティラッパーとして提供し、内部的には同じUIKitの復元経路に接続されています。
状態遷移の要点は「Suspendedへの遷移・そこからの終了はコールバックを伴わない」ことです。したがって永続化すべきデータはBackground移行時(applicationDidEnterBackground)までに保存し切る設計が原則になります。BackgroundとSuspendedを「どちらも見えないところで動いている」と混同しないこと、そしてメモリ逼迫による強制終了はクラッシュではなく正常なライフサイクル管理である点も頻出の整理ポイントです。
まとめ
iOSアプリはNot Running・Inactive・Active・Background・Suspendedの5状態を、着信やホーム画面遷移などのイベントに応じて移動し、各遷移でデリゲートメソッドが呼ばれます。BackgroundはOSが与える時間制限つきの実行猶予であるのに対し、Suspendedはメモリのみ保持しCPUを一切使わない凍結状態という明確な違いがあります。Suspended中のプロセスはメモリ逼迫時に通知なく終了させられますが、これは正常なライフサイクル管理であり、State Restorationの仕組みによって次回起動時にビュー階層とアプリ固有の状態を復元することで、ユーザー体験としては継続しているかのように見せています。この設計を前提にすると、永続化処理はSuspendedへの遷移を待たず、Background移行時点までに完了させておくべきだと分かります。より低いレイヤーのプロセス管理やメモリ回収の考え方は /os/ の話題とも重なります。
モバイル開発 Article
iOSアプリのライフサイクルと状態遷移を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
iOS
比較で見る軸
難易度: advanced / カテゴリ: モバイル開発 / タグ数: 5
導入後に効く点
Backgroundは時間制限つきの実行猶予、Suspendedはメモリに残るがCPUを一切使わない凍結状態で、両者は明確に区別される。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- モバイル開発
- タグ数
- 5
判断チェックリスト
- 自社の用途が「iOS / モバイル開発」に近いか確認する。
- 強みである「iOSアプリはNot Running/Inactive/Active/Background/Suspendedの5状態を遷移し、遷移のたびにUIApplicationDelegateのコールバックが呼ばれる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。