最近在开发中过程看到appDelegate中的一些方法,突然想到了之前了解但没有梳理过的app的生命周期的一些方法,因此在这里简单的记录并和大家分享一下iOS与macOS应用程序的生命周期。
在 Apple 生态系统中,无论是 iPhone 上的轻量级 App 还是 Mac 上功能完整的桌面应用,其行为都受到一套精密设计的"应用程序生命周期"机制控制。这套机制不仅决定了应用何时启动、暂停、恢复或终止,还深刻影响着内存管理、后台执行策略、多任务处理能力以及用户体验流畅度。
本文将从底层原理到实践层面,全面剖析 iOS 和 macOS 应用程序生命周期 的每一个关键环节,涵盖状态模型、代理方法调用顺序、多场景支持、后台任务调度、调试技巧,并对比两个平台的设计哲学差异。
一、为什么需要理解应用生命周期?
许多看似随机的问题------如数据丢失、定时器未停止、音频中断、定位服务异常退出------往往源于对生命周期回调的误用或忽略。
掌握生命周期意味着你可以:
- 在正确时机初始化资源
- 避免内存泄漏
- 实现优雅的数据持久化
- 提升电池效率
- 支持多窗口与多任务交互(尤其在 iPadOS/macOS)
- 适配 SwiftUI 和 UIKit/AppKit 混合架构
二、iOS 应用程序生命周期详解
1. 核心概念:UIApplication 与 Run Loop
iOS 应用基于 事件驱动模型 ,由 UIApplication
单例对象主导整个生命周期。它依赖于 Main Run Loop 来接收并分发事件(触摸、网络回调、定时器等)。
📌 Run Loop 是什么?
它是一个循环线程结构,持续监听输入源(Input Sources),并在有事件时唤醒线程执行任务。它是所有 UI 更新和用户交互的基础。
swift
// UIApplication 主循环简化示意(非真实实现)
func run() {
while isRunning {
let event = nextEvent()
if let e = event {
processEvent(e)
}
}
}
2. iOS 应用五大状态详解
状态 | 描述 | 可执行操作 | 是否会被系统终止 |
---|---|---|---|
Not Running | 进程未启动或已被杀死 | 无 | 是 |
Inactive | 运行但不接收事件(来电、通知中心、Control Center 弹出) | 可继续运行代码,但不处理 UI 事件 | 否 |
Active | 正在前台运行,完全响应用户输入 | 全功能运行 | 否 |
Background | 进入后台,系统给予约 3 秒宽限期执行清理任务 | 可申请延长后台执行时间(最多 3 分钟) | 是(超时后) |
Suspended | 被挂起,不消耗 CPU,保留在内存中 | 不执行任何代码 | 是(低内存时) |
⚠️ 注意:
- "Suspended" ≠ "Terminated"。挂起的应用仍驻留内存,可快速恢复。
- 系统可在任何时候终止 Suspended 或 Background 中的任务,不会调用
applicationWillTerminate
。
3. 生命周期方法详解(AppDelegate)
didFinishLaunchingWithOptions(_:)
这是应用的第一个入口点。在此处应完成:
- 初始化第三方 SDK(Firebase、Analytics)
- 设置根视图控制器
- 检查启动选项(例如通过 URL Scheme 启动)
swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
guard let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem else {
// 正常启动
setupRootViewController()
return true
}
// 处理 3D Touch 快捷方式启动
handleShortcutItem(shortcutItem)
return true
}
applicationDidBecomeActive(_:)
应用进入 Active 状态,表示可以正常交互。
建议操作:
- 恢复动画、AVPlayer 播放
- 重启 CADisplayLink / Timer
- 检查登录状态是否过期
避免:
- 执行耗时同步操作(阻塞主线程)
applicationWillResignActive(_:)
应用即将失去焦点。常见触发场景:
- 接到来电
- 用户打开通知中心/控制中心
- 切换到其他应用(双击 Home 键)
- Face ID 验证中断
建议操作:
- 暂停游戏、视频播放
- 暂停计时器
- 模糊敏感界面(安全考虑)
applicationDidEnterBackground(_:)
应用已进入后台。此时只有约 3 秒 时间执行任务。
若需更长时间运行(如上传文件、同步数据),必须请求后台任务:
swift
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTask = application.beginBackgroundTask { [weak self] in
// 超时回调:必须在此结束任务
self?.endBackgroundTask()
}
// 执行后台任务
performLongRunningTask { [weak self] in
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
if backgroundTask != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
📌 Tips:
- 每个后台任务最长 ~3 分钟(具体时间由系统动态调整)
- 使用
BGProcessingTaskRequest
(iOS 13+)进行低优先级后台处理(需声明 capability)
applicationWillEnterForeground(_:)
应用即将回到前台。这是刷新 UI 的理想时机。
swift
func applicationWillEnterForeground(_ application: UIApplication) {
// 刷新首页内容
NotificationCenter.default.post(name: .appWillEnterForeground, object: nil)
}
applicationWillTerminate(_:)
⚠️ 重要警告 :该方法仅在非挂起状态下被调用 !
即:当应用处于 Inactive 或 Background 状态时被手动杀死才会触发。大多数情况下(挂起后被系统清理),此方法不会执行。
因此,不要依赖它来保存关键数据!
正确做法:
- 在
applicationDidEnterBackground
中保存 - 使用自动保存机制(Core Data 自动保存、UserDefaults.flush())
- 监听特定事件即时保存
4. iOS 13+ 多场景(Scene-Based Lifecycle)
随着 iPadOS 支持多窗口、Mac Catalyst 的推出,Apple 引入了 Scene 架构,将 UI 与生命周期解耦。
关键组件:
UIScene
:代表一个独立的 UI 实例(如一个窗口)UISceneSession
:持久化场景信息(用于恢复)UISceneDelegate
:管理单个场景的生命周期NSUserActivity
:跨设备 Handoff 和场景恢复
Scene 生命周期方法(SceneDelegate.swift)
swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = createInitialViewController()
self.window = window
window.makeKeyAndVisible()
// 处理通过场景启动的快捷方式或 URL
handleConnectionOptions(connectionOptions)
}
func sceneDidBecomeActive(_ scene: UIScene) {
print("Scene became active")
}
func sceneWillResignActive(_ scene: UIScene) {
print("Scene will resign active")
}
func sceneDidEnterBackground(_ scene: UIScene) {
// 场景进入后台(可能还有其他场景在前台)
(scene as? UIWindowScene)?.windows.forEach { $0.resignFirstResponder() }
}
}
多场景配置(Info.plist)
xml
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
💡 提示:即使你的 App 不支持多窗口,iOS 13+ 也会默认创建一个场景。
三、macOS 应用程序生命周期详解
macOS 使用 AppKit 框架 ,其生命周期由 NSApplication
和 NSApplicationDelegate
控制。
1. 状态模型(较宽松)
状态 | 说明 |
---|---|
Not Running | 未运行 |
Inactive | 窗口未聚焦(其他应用在前台) |
Active | 当前应用拥有焦点 |
❗ macOS 没有"挂起"状态。应用可在后台无限期运行(除非用户主动退出或系统因内存压力终止)。
2. 生命周期方法(AppDelegate.swift)
swift
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
// 创建主窗口
let mainWindow = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered,
defer: false
)
mainWindow.center()
mainWindow.title = "My Mac App"
mainWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
// 如果没有可见窗口,重新打开主窗口
showMainWindow()
}
return true
}
func applicationWillTerminate(_ notification: Notification) {
// 清理资源、保存偏好设置
UserDefaults.standard.synchronize()
}
func applicationDidResignActive(_ notification: Notification) {
// 当前应用失去焦点
}
func applicationDidBecomeActive(_ notification: Notification) {
// 当前应用获得焦点
}
}
3. 窗口生命周期(NSWindowDelegate)
macOS 强调多窗口管理,每个窗口有自己的代理:
swift
extension MainWindowController: NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
// 保存窗口位置、大小
UserDefaults.standard.set(window?.frame, forKey: "MainWindowFrame")
}
}
4. 与 iOS 的关键区别
特性 | iOS | macOS |
---|---|---|
后台运行 | 严格限制 | 几乎无限制 |
终止机制 | 系统可随时终止 | 用户主动退出为主 |
多实例 | 通常单实例 | 可打开多个文档窗口 |
生命周期粒度 | 应用级 + 场景级 | 应用级 + 窗口级 |
用户期望 | 快速启动、节省电量 | 持续可用、功能完整 |
四、跨平台开发注意事项(SwiftUI & Catalyst)
1. SwiftUI 的统一生命周期
使用 SwiftUI 时,可通过 @UIApplicationDelegateAdaptor
或直接使用声明式生命周期:
swift
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: phase) { newPhase in
switch newPhase {
case .active:
print("App is active")
case .inactive:
print("App is inactive")
case .background:
print("App in background")
@unknown default:
break
}
}
}
}
2. Mac Catalyst 适配要点
- 启用
Target > General > Mac > Mac Catalyst
- 处理鼠标悬停、右键菜单、键盘快捷键
- 调整布局以适应更大屏幕
- 注意后台任务权限差异
五、调试生命周期问题的实用技巧
1. 使用 Xcode 生命周期断点
在 AppDelegate
方法中添加断点,观察调用顺序。
2. 模拟状态变化
Xcode → Debug → Simulate Background Fetch / Terminate App
3. 日志追踪
统一日志宏:
swift
func logLifecycle(_ message: String) {
print("[LIFECYCLE] \(Date()): \(message)")
}
并在各回调中调用。
4. Instruments 工具检测
使用 Energy Log 和 Allocations 查看后台能耗与内存占用。
六、常见陷阱与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
数据未保存 | 依赖 willTerminate |
改为 didEnterBackground 或实时保存 |
定时器持续运行 | 未在 resignActive 中暂停 |
使用 invalidate() 并在 becomeActive 重建 |
音频中断 | 未处理 AVAudioSession |
注册中断通知并恢复播放 |
内存警告后崩溃 | 未释放缓存 | 实现 didReceiveMemoryWarning |
多场景重复初始化 | 未检查 session restoration ID | 使用 session.persistentIdentifier 去重 |
七、总结
平台 | 设计哲学 | 生命周期特点 | 开发建议 |
---|---|---|---|
iOS | 移动优先、资源受限、用户体验至上 | 严格状态管理、后台限制、自动挂起 | 快速响应、及时保存、合理使用后台任务 |
macOS | 功能完整、多任务、长期运行 | 松散状态、自由后台、多窗口 | 注重窗口管理、支持文档模型、优化长期运行性能 |
尽管 iOS 和 macOS 在生命周期实现上存在差异,但其核心理念一致:让应用在合适的时机做合适的事。
作为开发者,我们应当:
- 理解状态转换逻辑
- 正确使用生命周期钩子
- 善用工具调试
- 面向未来设计(支持多场景、Catalyst、SwiftUI)
只有这样,才能构建出既高效又可靠的跨平台应用。
延伸阅读与官方文档
- Apple Developer: App Programming Guide for iOS
- Apple Developer: App Architecture
- WWDC 2019 Session 210: Introducing SwiftUI
- WWDC 2019 Session 227: Modernizing Your App's Lifecycle
- Apple Human Interface Guidelines - Multitasking (iPadOS)
📌 结语
应用生命周期不仅是技术细节,更是设计思维的体现。掌握它,你就能更好地驾驭 Apple 平台的强大能力,为用户带来无缝、流畅、可靠的体验。
如果你觉得这篇文章有价值,请分享给你的团队!也欢迎在评论区提出疑问或分享你的最佳实践。