iOS 中的 ViewController Lifecycle:关键概念、阶段和方法

作者: IT Sectr 发布日期: 2026-03-05 阅读时间: 9 分钟

ViewController Lifecycle — 是 UIKit 在 iOS 中管理屏幕时自动调用的方法序列。根据 Apple Documentation,每个 UIViewController 都会经历一组可预测的状态:从 View 的创建到它的出现和隐藏。理解这些方法的顺序和目的是 iOS 应用程序稳定运行的必要条件。

要点

  • ViewController Lifecycle — 是 UIKit 按严格顺序调用的六个 UIViewController 方法
  • loadView 在不使用 Storyboard 时创建 View 层次结构
  • viewDidLoad 只调用一次,适用于屏幕的初始设置
  • viewWillAppearviewDidAppear 在每次出现时触发
  • viewWillDisappearviewDidDisappear — 用于保存状态和清理

什么是 ViewController Lifecycle

ViewController Lifecycle — 是 UIViewController 在其存在期间从 UIKit 接收的一组方法。iOS 应用程序中的每个屏幕依次经历创建、View 加载、屏幕显示、消失和内存释放的阶段。UIKit 在每个阶段自动调用相应的方法,开发者通过重写这些方法来添加自己的逻辑。

UIViewController 的架构是 UIKit 的基础,即使在 SwiftUI 时代仍然具有相关性 — 许多项目仍然使用经典方法或混合架构。理解 Lifecycle 可以预测子视图何时可用、何时可以安全地修改布局以及在屏幕显示或隐藏时执行哪些操作。

每个生命周期方法都有特定的用途:有些在控制器的整个存在期间只调用一次,其他的则在每次出现或消失时调用。在方法之间混合逻辑会导致难以发现的错误:内存泄漏、数据更新不正确和不必要的网络请求。

UIViewController 方法的完整周期

六个方法构成 UIViewController 的完整生命周期。它们的调用顺序是固定的,不依赖于导航方式 — push、present 或 unwind segue 都遵循相同的调度。

loadView — 创建根 View

loadView — 周期的第一个方法,在控制器的 View 尚不存在时调用。如果您使用 Storyboard,UIKit 会自动从 xib 文件加载 View。在通过编程创建界面时,您重写此方法,手动分配根 View。在大多数项目中,loadView 不会被触及 — 工作是在 viewDidLoad 中完成的。

只有在特定情况下才需要重写 loadView:当整个界面在没有 Storyboard 的情况下通过代码创建,或者根 View 必须是非标准类时。Apple 建议在重写时不要调用 super.loadView — 您完全接管 View 的创建。

swift
override func loadView() {
    view = UIView()
    view.backgroundColor = .white
}

viewDidLoad — 一次性初始化

viewDidLoad — 周期中最常用的方法。在 View 加载到内存之后但在显示在屏幕上之前调用一次。在这里配置子视图、用数据填充表格、注册单元格以及订阅在控制器整个生命周期内有效的通知。

重要特性:viewDidLoad 在屏幕再次显示时不会再次调用。如果您需要在每次出现时更新数据 — 请使用 viewWillAppear。在 viewDidLoad 中只放置依赖于基本配置的一次性操作。

viewWillAppear — 显示前的准备

viewWillAppear 每次在 View 对用户变为可见之前调用。此方法接收 animated 参数,指示出现是否带有动画。在这里更新数据、重新加载表格、配置 NavigationBar 以及根据应用程序状态隐藏或显示元素。

使用 viewWillAppear 在屏幕之间同步状态:如果用户可能在前一个屏幕上更改了数据,此方法是更新界面的正确位置。每次 viewWillAppear 调用都在屏幕出现之前发生,即使是从子控制器返回时也是如此。

viewDidAppear — 屏幕完全可见

viewDidAppear 通知 View 已完全出现在屏幕上并且所有过渡动画已完成。此时,屏幕已准备好进行交互 — 用户看到完整的界面并可以与之交互。此方法适用于启动应在出现后开始的动画、启动计时器和跟踪分析展示。

与 viewWillAppear 不同,viewDidAppear 保证屏幕不仅可见而且完全渲染。如果您在 viewWillAppear 中启动动画,一些帧可能会被跳过,因为 UIKit 尚未完成过渡。对于流畅的动画,请使用 viewDidAppear。

viewWillDisappear — 准备隐藏

viewWillDisappear 在 View 从屏幕消失之前调用 — 切换到另一个控制器、关闭模态窗口或最小化应用程序时。这是保存状态、取消订阅通知、停止活动进程和释放屏幕不可见时不需要的资源的正确位置。

重要提示:viewWillDisappear 不保证 View 最终会消失 — 手势可能被取消。因此,也要在 viewDidDisappear 中保存关键数据,后者仅在实际消失后调用。

viewDidDisappear — 屏幕已隐藏

viewDidDisappear 结束出现和消失的周期。在 View 已从屏幕隐藏后调用。在此方法中,动画完全停止,临时对象被删除,并且确认了在 viewWillDisappear 中开始的数据保存。

此方法也在控制器的 deinit 之前调用 — 如果您的 UIViewController 被销毁,viewDidDisappear 将是 deinit 调用之前的最后一个 Lifecycle 方法。在对象销毁之前,使用它进行最终清理。

每个方法何时被调用

调用顺序取决于屏幕出现的具体方式:第一次、返回时或模态显示时。让我们从 UIKit 的角度来看三个主要场景。

首次打开时的顺序

在屏幕第一次出现时,UIKit 经历完整的创建周期:调用 loadView,然后 viewDidLoad,之后出现动画开始。在动画期间调用 viewWillAppear,完成后调用 viewDidAppear。这是所有方法从 loadView 到 viewDidAppear 依次调用的唯一场景

swift
override func viewDidLoad() {
    super.viewDidLoad()
    print("viewDidLoad — View 已加载到内存")
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    print("viewWillAppear — 即将出现")
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print("viewDidAppear — 屏幕完全可见")
}

返回时的顺序

当用户返回到前一个屏幕时,UIKit 不会再次调用 viewDidLoad — View 已加载到内存中。相反,在返回的屏幕上只触发 viewWillAppear 和 viewDidAppear,在当前屏幕上触发 viewWillDisappear 和 viewDidDisappear。loadView 和 viewDidLoad 被跳过,因为屏幕已经存在于导航堆栈中。

present 和 dismiss 的特点

模态显示遵循相同的规则:新控制器在第一次出现时调用完整周期,当前控制器调用 viewWillDisappear 和 viewDidDisappear。在 dismiss 时顺序相反:返回的控制器再次触发 viewWillAppear 和 viewDidAppear,被隐藏的控制器触发结束方法。此行为对于 UIKit 中的所有过渡类型都是统一的

实际使用场景

让我们看看四个关键场景,其中理解 Lifecycle 直接影响代码质量和用户体验。对于每个场景,我们提供带有建议的示例。

在 viewDidLoad 中初始化数据

viewDidLoad — 进行不依赖于屏幕可见性的初始设置的位置。在这里配置 collectionView、注册单元格的 nib 文件、创建数据源和布局。如果您从网络加载数据,在 viewDidLoad 中最好只发起请求,而在 viewWillAppear 中更新界面,当屏幕准备好显示时。

swift
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(
        MyCell.self,
        forCellReuseIdentifier: MyCell.identifier
    )
    viewModel.loadInitialData()
}

在 viewWillAppear 中更新内容

使用 viewWillAppear 在每次屏幕出现时同步数据。例如,如果用户可能在前一个屏幕上更改了设置,则在此处更新显示的值、重新加载表格并调整 NavigationBar 状态。这确保屏幕在任何导航场景下始终显示最新数据

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    tableView.reloadData()
    navigationController?.setNavigationBarHidden(false, animated: animated)
}

在 viewDidAppear 中进行分析和动画

viewDidAppear 非常适合启动应在用户看到屏幕后开始的动画。在此处还发送分析事件:屏幕展示、开始引导或开始视频播放。在过渡完成之前启动动画会导致卡顿的界面 — UIKit 来不及准备足够的帧。

在 viewWillDisappear 中保存状态

viewWillDisappear 中保存草稿、停止计时器并取消订阅 NotificationCenter。这是屏幕仍然可见并且可用于需要用户上下文操作的最后时刻。对于关键数据,额外使用 viewDidDisappear 作为针对取消手势的保险。

使用 Lifecycle 时的常见错误

生命周期方法的错误使用是 iOS 应用程序中最常见的错误来源之一。让我们看看开发人员在不同阶段使用 UIViewController 时的主要错误。

第一个错误 — 在使用 Storyboard 时在 init 或 loadView 中创建子视图。如果您使用 Interface Builder,不要无故重写 loadView。在存在 storyboard 的情况下在 loadView 中创建 View 会导致 xib 文件被忽略和空屏幕。

第二个错误 — 在 viewDidLoad 中订阅键盘通知而不取消订阅。如果您订阅了 UIResponder.keyboardWillShowNotification 但在屏幕隐藏时没有取消订阅,代码块将在控制器 deinit 之后仍然被调用 — 这是内存泄漏,可能导致应用程序崩溃。

第三个错误 — 在屏幕出现之前启动的计时器和网络请求。当 View 尚不可见时加载图像或运行动画 — 浪费资源。将视觉更新移到 viewWillAppear 或 viewDidAppear 中。

第四个错误 — 仅在 viewWillDisappear 中保存数据。在交互式 pop 手势中,用户可以开始滑动并取消它 — 方法被调用但屏幕没有消失。在 viewDidDisappear 或 applicationDidEnterBackground 的处理程序中复制关键保存。

常见问题

viewDidLoad 在控制器的生命周期中被调用多少次?

一次 — 在 View 加载到内存之后。在屏幕再次出现时,viewDidLoad 不会被调用。如果需要重新创建 View,控制器必须被销毁并重新创建。

如果在 viewDidLoad 中不调用 super 会怎样?

UIKit 要求调用 super.viewDidLoad 以保证生命周期正常运作。没有它,可能会出现布局更新和过渡处理的问题。始终在方法中首先调用 super。

可以同时使用 Storyboard 和编程式 loadView 吗?

不建议。如果控制器是从 Storyboard 初始化的,UIKit 会自动从 xib 加载 View。重写 loadView 会取消此过程,您的 storyboard 将被忽略。

如何正确地从 NotificationCenter 取消订阅?

viewDidLoad 或 viewWillAppear 中订阅,在 viewWillDisappear 或 viewDidDisappear 中取消订阅,使用对 self 的弱引用以避免闭包中的内存泄漏。

为什么 viewDidDisappear 在强制退出时不会被调用?

强制退出会强行终止进程 — UIKit 来不及调用 Lifecycle 方法。要保存数据,请在 AppDelegate 中使用 UIApplication.willTerminateNotification 通知。

总结

  • ViewController Lifecycle 由 UIKit 按固定顺序调用的六个方法组成
  • loadViewviewDidLoad 在创建控制器时触发一次
  • viewWillAppearviewDidAppear 在每次屏幕出现时调用
  • viewWillDisappearviewDidDisappear — 在每次隐藏时调用
  • 每个方法都有特定的用途 — 混合逻辑会导致错误
  • 通知订阅始终通过在相应方法中取消订阅来平衡
  • 使用 viewDidAppear 进行动画和分析,使用 viewWillDisappear 保存状态

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读