夜雨聆风学习资料网

ARTICLE · 1268016

iOS 应用生命周期:五个状态与六个回调

iOS 应用生命周期:五个状态与六个回调
当你点击手机上微信图标,看到地球和小人的启动页,再看到好友列表。中间过程到底经历了什么?
今天站在专业视角,讲清楚iOS 这一套固定规则编排的状态机。理解这套状态机,是写出不卡顿、不丢数据、不被系统杀掉的应用的前提。
本文把五个状态、AppDelegate 的六个关键时刻、iOS 13 之后的 SceneDelegate 分治,以及三个最常被忽略的坑一次性讲清。

一、五个状态:把应用看成状态机

一个 iOS 应用在任何时刻都处于下列五种状态之一,系统负责在它们之间切换,开发者只能在切换的「缝隙」里插手。
状态
在前台
接收事件
执行代码
Not Running
否
否
否
Inactive
是
否
是
Active
是
是
是
Background
否
否
是(有限)
Suspended
否
否
否
最容易混淆的是Inactive和Suspended:Inactive 仍在前台、代码还在跑,只是暂时不收事件(如来电打断游戏);Suspended 已经被系统冻结,代码彻底停摆,只是内存里还留着它的「尸体」,方便你下次回来秒开。
从启动到退场的典型路径:
Not Running ↓ (用户点图标 / 系统拉起) didFinishLaunching → Active ↓ (来电 / 切后台) WillResignActive → Background ↓ (无任务可跑) Suspended (内存紧张时直接被系统杀掉 → Not Running) ↑ (用户切回) WillEnterForeground → Active

二、AppDelegate 的六个关键时刻

UIKit 用 AppDelegate 的六个方法告诉你「状态要变了」。下面是一份可直接抄进项目的骨架,关键注释写明了每个钩子里该做什么、不该做什么。
import UIKitclass AppDelegate: UIResponder, UIApplicationDelegate {    var window: UIWindow?    // 1. 启动入口:只做轻量一次性初始化    //    重活(网络、大文件解析)别放这,会拖慢首屏,超时被看门狗杀掉    func application(_ application: UIApplication,                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {        window = UIWindow(frame: UIScreen.main.bounds)        window?.rootViewController = ViewController()        window?.makeKeyAndVisible()        return true    }    // 2. 进入前台并准备收事件:恢复被暂停的任务    func applicationDidBecomeActive(_ application: UIApplication) {        // 重启音乐、重建网络长连接、刷新 UI    }    // 3. 即将失去活跃:暂停不该在后台跑的东西    func applicationWillResignActive(_ application: UIApplication) {        // 暂停游戏循环、停止视频播放、冻结计时器    }    // 4. 已进后台:保存数据、申请后台任务    //    若支持后台执行,用户「退出」走的是这里,不是 willTerminate    func applicationDidEnterBackground(_ application: UIApplication) {        UserDefaults.standard.set("saved", forKey: "appState")        // 需要长时间任务时调用 beginBackgroundTask(withName:expirationHandler:)    }    // 5. 从后台回到前台:撤销后台里做的改动    func applicationWillEnterForeground(_ application: UIApplication) {        // 刷新受后台影响的 UI、重新拉取数据    }    // 6. 即将终止:仅有的保存窗口,但「不保证被调用」    func applicationWillTerminate(_ application: UIApplication) {        // 只做最小化清理,别依赖它保数据    }}
一个常被问的问题:用户上划杀掉 app,会走 willTerminate 吗?不会。用户强制退出时系统直接发 SIGKILL,任何回调都不走。willTerminate 只在系统因内存压力回收一个 Suspended 应用、或极少数正常退出路径下才触发。

三、iOS 13 之后:SceneDelegate 接管多窗口

iPadOS 多窗口和「台前调度」出现后,Apple 把「应用」和「场景」拆开了:一个进程可以有多个 Scene,每个 Scene 有自己独立的生命周期。于是原来 AppDelegate 里那套 active/background 回调,被搬到了SceneDelegate。
这一改带来一个实际坑:如果你的项目同时实现了 AppDelegate 和 SceneDelegate,前/后台的状态切换回调会落在 SceneDelegate 上,AppDelegate 的同名方法不再为场景切换触发。AppDelegate 只保留进程级的两件事:
进程启动:application(_:didFinishLaunchingWithOptions:)
进程终止:applicationWillTerminate(_:)
而场景级的六个方法镜像对应:
scene(_:willConnectTo:options:)        // 场景创建,等价于启动入口sceneDidBecomeActive(_:)sceneWillResignActive(_:)sceneDidEnterBackground(_:)sceneWillEnterForeground(_:)sceneDidDisconnect(_:)                  // 场景被销毁(不是 app 退出)
判断该写哪一层很简单:跟「单个窗口的 UI 状态」有关就进 SceneDelegate;跟「整个进程只需做一次」有关(如全局配置、第三方 SDK 初始化)才放 AppDelegate。

四、三个最常被忽略的坑

1. 把 willTerminate 当数据保险箱
它不保证调用,用户强杀和内存回收都绕开它。正确做法是「增量保存」:每次关键操作落盘,或在 didEnterBackground 里就存好,willTerminate 只做收尾。
2. 忘记在 willResignActive 里暂停
来电、控制中心下拉、双击 Home 都会让应用 Inactive。不在这里停掉视频/GPS/计时器,后台会空耗电量和定位权限,用户直接在设置里把权限关了。
3. 误以为 Suspended 是安全的「暂停」
Suspended 只是系统「暂时没杀你」。一旦内存紧张,Suspended 应用是第一批被回收的对象,而且不通知、不回调。所以进后台那一刻就得把可恢复状态存好,别等「下次回来还在」。

五、进阶:后台模式与状态恢复

Background Modes决定应用被允许在后台干多久。音频播放、位置更新、VoIP、后台刷新等都需要在Info.plist的UIBackgroundModes里声明,否则系统会直接挂起你。注意:后台时间窗口有限(通常 30 秒起,按任务类型延长),超时仍会被冻。
State Restoration是让应用「杀掉后重开像没关过」的机制:系统会在合适时机编码你的视图控制器栈,下次启动自动还原。配合在 didEnterBackground 里存好业务数据,用户几乎感知不到中断。
URL Schemes / Universal Links则反过来——别的应用或网页带着数据把你拉起来。这种「冷启动」路径下,launchOptions里会带着来源信息,需要在 didFinishLaunching 里就分流处理,而不是等界面出来再补。
生命周期不是一组回调方法的堆砌,它是应用与系统、与用户交互的基本契约。把状态转换想清楚,首屏更快、切后台不丢数据、被杀能恢复——这三件事做好了,应用的「体质」就稳了。

相关学习资料