Swift 视图控制器生命周期源码深度解析

一、视图控制器生命周期概述

视图控制器(UIViewController)作为iOS开发的核心组件,负责管理应用界面的呈现与交互逻辑。其生命周期涵盖从创建到销毁的完整过程,理解这一过程对于开发高效、稳定的iOS应用至关重要。

1.1 生命周期的基本概念

UIViewController的生命周期是指其从创建到最终被系统回收的整个过程。这个过程涉及多个状态转换和关键方法调用,开发者可以通过重写这些方法来插入自定义逻辑。生命周期管理不仅影响界面的正确显示,还直接关系到内存使用效率和应用性能。

1.2 生命周期研究的重要性

深入理解UIViewController的生命周期有助于解决以下开发中的关键问题:

  • 内存管理:避免内存泄漏和过度占用
  • 界面渲染:确保视图正确加载和更新
  • 用户交互:处理好视图可见性变化时的交互状态
  • 资源优化:在适当的时机释放或获取资源

二、视图控制器的初始化过程

视图控制器的生命周期始于初始化阶段,这个阶段决定了控制器的初始状态和配置。

2.1 初始化方法解析

UIViewController提供了多种初始化方式,每种方式都有其特定的使用场景和源码实现。

// 最基本的初始化方法
public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)// 通过Storyboard初始化
public init?(coder: NSCoder)// SwiftUI集成的初始化方法
@available(iOS 13.0, *)
public init(rootView: Content) where Content : View

2.2 初始化流程源码分析

当初始化一个视图控制器时,系统会执行一系列操作来设置其基本状态。以下是初始化流程的关键步骤:

// UIViewController初始化核心流程简化版
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {// 1. 调用父类初始化方法super.init()// 2. 设置nib名称和bundleself.nibName = nibNameOrNilself.nibBundle = nibBundleOrNil// 3. 初始化视图控制器的基本属性_commonInit()// 4. 注册KVO和通知监听_registerForNotifications()
}

2.3 不同初始化方式的差异

不同的初始化方法在源码实现上有细微但重要的差异:

  • init(nibName:bundle:):手动指定nib文件加载视图
  • init(coder:):从归档数据中恢复视图控制器,用于Storyboard和XIB
  • init(rootView:):专为SwiftUI集成设计,使用SwiftUI视图作为内容

这些差异会影响后续的视图加载和生命周期行为。

三、视图加载阶段解析

视图加载是视图控制器生命周期中的关键阶段,负责创建和准备视图层次结构。

3.1 loadView方法源码分析

loadView是视图加载的核心方法,其源码实现揭示了视图控制器如何创建其视图:

// UIViewController的loadView方法简化版
func loadView() {// 1. 检查是否通过nibName指定了nib文件if let nibName = self.nibName {let nibBundle = self.nibBundle ?? Bundle.mainlet nib = UINib(nibName: nibName, bundle: nibBundle)// 2. 从nib加载视图let views = nib.instantiate(withOwner: self, options: nil)if let view = views.first as? UIView {self.view = view} else {fatalError("无法从nib文件加载视图")}} // 3. 检查是否使用Storyboardelse if let storyboard = self.storyboard {// 从Storyboard加载视图let view = storyboard.instantiateInitialViewController()?.viewself.view = view ?? UIView()} // 4. 创建空视图else {self.view = UIView(frame: UIScreen.main.bounds)}// 5. 设置视图的默认属性_setupViewProperties()
}

3.2 viewDidLoad方法详解

viewDidLoad在视图加载完成后立即调用,是开发者插入自定义初始化代码的重要时机:

// UIViewController的viewDidLoad方法
func viewDidLoad() {super.viewDidLoad()// 视图控制器子类可以重写此方法添加自定义逻辑// 例如:初始化视图属性、设置数据源、添加子视图等// 调用自定义初始化钩子_performCustomInitialization()
}

3.3 视图加载的不同场景

视图加载可能发生在多种场景下,包括:

  • 首次访问view属性
  • 从内存中重新加载(如内存警告后)
  • 通过Storyboard或XIB加载
  • 以编程方式创建视图层次结构

每种场景下的加载流程略有不同,但核心逻辑保持一致。

四、视图布局与尺寸调整

视图加载完成后,需要进行布局和尺寸调整以适应不同的设备和方向。

4.1 viewWillLayoutSubviews源码分析

viewWillLayoutSubviews在视图即将布局其子视图时调用:

// UIViewController的viewWillLayoutSubviews方法
func viewWillLayoutSubviews() {super.viewWillLayoutSubviews()// 1. 通知视图即将布局view.sendSubviewLayoutNeedSending()// 2. 调用约束更新逻辑_updateViewConstraintsIfNeeded()// 3. 调用自定义布局准备逻辑_prepareForCustomLayout()
}

4.2 viewDidLayoutSubviews详解

viewDidLayoutSubviews在视图完成布局后调用:

// UIViewController的viewDidLayoutSubviews方法
func viewDidLayoutSubviews() {super.viewDidLayoutSubviews()// 1. 确保所有子视图都已完成布局view.layoutIfNeeded()// 2. 通知子类布局完成_notifyLayoutComplete()// 3. 执行任何需要在布局后完成的操作_performPostLayoutOperations()
}

4.3 自动布局与手动布局的实现差异

自动布局和手动布局在源码实现上有显著差异:

// 自动布局实现示例
func setupAutoLayout() {// 创建约束view.addConstraints([subview.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),subview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),subview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),subview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)])// 激活约束NSLayoutConstraint.activate(constraints)
}// 手动布局实现示例
func setupManualLayout() {// 在viewDidLayoutSubviews中实现override func viewDidLayoutSubviews() {super.viewDidLayoutSubviews()// 计算并设置子视图的framesubview.frame = CGRect(x: 20, y: 20, width: view.bounds.width - 40, height: view.bounds.height - 40)}
}

五、视图的可见性转换

视图控制器的可见性转换是生命周期中的重要环节,涉及视图的显示、隐藏和移除。

5.1 viewWillAppear方法源码

viewWillAppear在视图即将变为可见之前调用:

// UIViewController的viewWillAppear方法
func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 1. 更新视图控制器的可见状态_isBeingPresented = true// 2. 准备视图动画_prepareViewForAppearance(animated: animated)// 3. 通知观察者视图即将出现_notifyObserversViewWillAppear()// 4. 更新导航栏状态_updateNavigationBarAppearance()
}

5.2 viewDidAppear方法详解

viewDidAppear在视图完全变为可见之后调用:

// UIViewController的viewDidAppear方法
func viewDidAppear(_ animated: Bool) {super.viewDidAppear(animated)// 1. 更新视图控制器的可见状态_isBeingPresented = false_isVisible = true// 2. 完成视图动画_completeViewAppearanceAnimation(animated: animated)// 3. 通知观察者视图已出现_notifyObserversViewDidAppear()// 4. 开始任何需要在视图可见时运行的任务_startVisibleStateTasks()
}

5.3 视图消失相关方法

视图消失的过程与出现类似,但方向相反:

// viewWillDisappear方法
func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 1. 更新可见状态_isBeingDismissed = true// 2. 准备视图消失动画_prepareViewForDisappearance(animated: animated)// 3. 暂停或停止可见时运行的任务_suspendVisibleStateTasks()
}// viewDidDisappear方法
func viewDidDisappear(_ animated: Bool) {super.viewDidDisappear(animated)// 1. 更新可见状态_isBeingDismissed = false_isVisible = false// 2. 完成视图消失动画_completeViewDisappearanceAnimation(animated: animated)// 3. 释放视图不可见时不需要的资源_releaseInvisibleResources()
}

六、内存管理与低内存处理

视图控制器的内存管理是生命周期中的关键部分,尤其是在处理低内存情况时。

6.1 didReceiveMemoryWarning方法源码

当系统内存不足时,会调用此方法:

// UIViewController的didReceiveMemoryWarning方法
func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()// 1. 检查视图控制器是否可见if !_isVisible {// 2. 如果不可见,释放视图以节省内存if _canReloadView {_unloadViewForced: true)}}// 3. 释放其他可恢复的资源_releaseOptionalResources()// 4. 通知观察者内存警告_notifyObserversMemoryWarningReceived()
}

6.2 视图卸载与重新加载机制

当内存不足时,不可见的视图控制器可能会卸载其视图:

// 视图卸载方法
func _unloadView(forced: Bool) {// 1. 检查是否可以卸载视图if !forced && !_shouldUnloadView {return}// 2. 保存视图状态_saveViewState()// 3. 通知视图即将卸载_notifyViewWillUnload()// 4. 移除所有子视图view.subviews.forEach { $0.removeFromSuperview() }// 5. 释放视图引用view = nil// 6. 通知视图已卸载_notifyViewDidUnload()
}// 视图重新加载方法
func _reloadViewIfNeeded() {if view == nil {// 重新调用loadView方法loadView()// 重新触发viewDidLoadviewDidLoad()// 恢复视图状态_restoreViewState()}
}

6.3 内存管理最佳实践

基于源码分析,我们可以总结出以下内存管理最佳实践:

  • 在viewDidUnload中释放非必要资源(iOS 9及以前)
  • 使用弱引用避免循环引用
  • 在didReceiveMemoryWarning中释放可恢复资源
  • 优先使用懒加载模式创建视图和资源

七、旋转与多任务处理

随着iOS设备支持多任务和屏幕旋转,视图控制器需要处理这些场景下的生命周期变化。

7.1 屏幕旋转处理

当设备方向改变时,视图控制器会经历一系列方法调用:

// 旋转相关方法调用顺序
willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {super.willTransition(to: newCollection, with: coordinator)// 1. 准备旋转_prepareForRotation(to: newCollection)// 2. 执行旋转动画coordinator.animate(alongsideTransition: { context in// 更新视图布局self._updateLayoutForRotation()}, completion: { context in// 旋转完成self._finalizeRotation()})
}// 方向已经改变
didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {super.didRotate(from: fromInterfaceOrientation)// 执行旋转后的操作_performPostRotationTasks()
}

7.2 多任务处理支持

在多任务模式下,视图控制器需要处理尺寸变化:

// 尺寸变化处理方法
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {super.viewWillTransition(to: size, with: coordinator)// 1. 计算新的布局_calculateNewLayout(for: size)// 2. 执行动画过渡到新布局coordinator.animate(alongsideTransition: { context inself._applyNewLayout()}, completion: nil)
}

7.3 多窗口支持

iOS 13引入的多窗口支持对视图控制器生命周期有新的影响:

// 窗口场景相关方法
func windowScene(_ windowScene: UIWindowScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {// 窗口场景连接时调用_setupForWindowScene(windowScene)
}func windowSceneDidBecomeActive(_ windowScene: UIWindowScene) {// 窗口场景变为活跃状态_handleWindowSceneActivation()
}func windowSceneWillResignActive(_ windowScene: UIWindowScene) {// 窗口场景即将变为非活跃状态_prepareForWindowSceneResignation()
}

八、与导航控制器的集成

当视图控制器被嵌入到导航控制器中时,其生命周期会受到额外的影响。

8.1 导航控制器的push和pop操作

导航控制器的push和pop操作会触发视图控制器的特定生命周期方法:

// 导航控制器push操作相关方法调用
// 在源视图控制器中:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {if let destination = segue.destination as? MyViewController {// 传递数据到目标视图控制器destination.data = self.selectedData}
}// 在目标视图控制器中:
override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 视图即将显示,可能是被push进来的
}// 导航控制器pop操作相关方法调用
// 在目标视图控制器中:
override func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 视图即将消失,可能是被pop出去的
}// 在源视图控制器中:
override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 视图即将重新显示,可能是子视图控制器被pop了
}

8.2 导航栏的管理

导航控制器会管理其包含的视图控制器的导航栏:

// 导航栏管理相关源码
func _updateNavigationBarAppearance() {// 1. 获取当前视图控制器的导航项let navigationItem = topViewController?.navigationItem// 2. 更新导航栏标题navigationBar.topItem?.title = navigationItem?.title// 3. 更新导航栏按钮navigationBar.topItem?.leftBarButtonItems = navigationItem?.leftBarButtonItemsnavigationBar.topItem?.rightBarButtonItems = navigationItem?.rightBarButtonItems// 4. 更新导航栏样式if let barTintColor = navigationItem?.barTintColor {navigationBar.barTintColor = barTintColor}// 5. 更新导航栏透明度navigationBar.isTranslucent = navigationItem?.hidesNavigationBarDuringTransition ?? false
}

8.3 自定义转场动画

导航控制器支持自定义转场动画,这会影响生命周期方法的调用时机:

// 自定义转场动画代理方法
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {if operation == .push {return CustomPushAnimator()} else if operation == .pop {return CustomPopAnimator()}return nil
}// 自定义动画器实现
class CustomPushAnimator: NSObject, UIViewControllerAnimatedTransitioning {func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {return 0.5}func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {// 实现自定义转场动画guard let toView = transitionContext.view(forKey: .to) else { return }let containerView = transitionContext.containerViewcontainerView.addSubview(toView)// 设置初始状态toView.frame.origin.x = containerView.bounds.width// 执行动画UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {toView.frame.origin.x = 0}) { finished intransitionContext.completeTransition(finished)}}
}

九、与标签栏控制器的集成

当视图控制器作为标签栏控制器的子控制器时,其生命周期会有特殊的表现。

9.1 标签切换的生命周期影响

标签切换会触发视图控制器的特定生命周期方法:

// 标签切换相关生命周期方法
// 当标签栏控制器选择新的视图控制器时
override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 检查是否因为标签切换而显示if isBeingPresented || isMovingToParent {// 首次显示或从其他标签切换过来_handleTabSelection()}
}// 当标签栏控制器切换到其他标签时
override func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 检查是否因为标签切换而隐藏if isBeingDismissed || isMovingFromParent {// 被其他标签替换_handleTabDeselection()}
}

9.2 标签栏控制器的管理

标签栏控制器管理其子控制器的方式:

// 标签栏控制器管理子控制器的源码
func _selectViewController(_ viewController: UIViewController, animated: Bool) {// 1. 检查是否已经选中if viewController == selectedViewController {return}// 2. 准备切换_prepareForViewControllerChange()// 3. 通知即将 deselected 的控制器if let oldController = selectedViewController {oldController.beginAppearanceTransition(false, animated: animated)_removeViewControllerFromViewHierarchy(oldController, animated: animated)oldController.endAppearanceTransition()}// 4. 更新选中状态selectedViewController = viewController// 5. 通知即将 selected 的控制器viewController.beginAppearanceTransition(true, animated: animated)_addViewControllerToViewHierarchy(viewController, animated: animated)viewController.endAppearanceTransition()// 6. 更新标签栏外观_updateTabBarAppearance()
}

9.3 标签栏的自定义与生命周期

自定义标签栏会影响视图控制器的布局和生命周期:

// 自定义标签栏控制器示例
class CustomTabBarController: UITabBarController {override func viewDidLoad() {super.viewDidLoad()// 自定义标签栏外观tabBar.barTintColor = UIColor.systemBluetabBar.tintColor = UIColor.whitetabBar.unselectedItemTintColor = UIColor.lightGray// 设置代理delegate = self}
}// 实现标签栏控制器代理方法
extension CustomTabBarController: UITabBarControllerDelegate {func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {// 可以在这里添加自定义逻辑,控制是否允许切换标签return true}func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {// 标签切换后执行的操作_performPostTabSelectionTasks()}
}

十、与模态呈现的交互

模态呈现是iOS应用中常用的视图控制器展示方式,它对生命周期有独特的影响。

10.1 模态呈现的基本流程

模态呈现涉及多个步骤和方法调用:

// 模态呈现的核心方法
func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {// 1. 检查是否已经在呈现其他视图控制器if isBeingPresented || presentedViewController != nil {// 处理错误或队列呈现请求return}// 2. 设置呈现上下文_setupPresentationContext(for: viewControllerToPresent)// 3. 通知即将呈现_notifyObserversWillPresent(viewControllerToPresent)// 4. 准备动画if flag {_preparePresentationAnimation(for: viewControllerToPresent)}// 5. 将视图控制器添加到视图层次结构_addChildViewController(viewControllerToPresent)// 6. 执行呈现动画if flag {_performPresentationAnimation(for: viewControllerToPresent) { [weak self] inself?._finalizePresentation(for: viewControllerToPresent)completion?()}} else {_finalizePresentation(for: viewControllerToPresent)completion?()}
}

10.2 模态呈现的生命周期方法调用

模态呈现和消失过程中涉及的生命周期方法:

// 呈现方视图控制器的方法调用
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {if let destination = segue.destination as? MyModalViewController {// 配置要呈现的视图控制器destination.delegate = self}
}override func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 检查是否因为呈现其他视图控制器而消失if isBeingPresented {// 正在被其他视图控制器覆盖} else if presentedViewController != nil {// 正在呈现其他视图控制器}
}// 被呈现视图控制器的方法调用
override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 视图即将通过模态呈现显示
}override func viewDidAppear(_ animated: Bool) {super.viewDidAppear(animated)// 视图已通过模态呈现完全显示
}

10.3 自定义模态转场动画

iOS支持自定义模态转场动画,这会影响生命周期方法的调用时机:

// 自定义模态转场动画实现
class CustomModalTransition: NSObject, UIViewControllerAnimatedTransitioning {func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {return 0.4}func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {guard let toView = transitionContext.view(forKey: .to),let fromView = transitionContext.view(forKey: .from) else { return }let containerView = transitionContext.containerViewlet isPresenting = transitionContext.viewController(forKey: .to)?.presentingViewController == transitionContext.viewController(forKey: .from)if isPresenting {// 呈现动画containerView.addSubview(toView)toView.alpha = 0.0toView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {toView.alpha = 1.0toView.transform = .identity}) { finished intransitionContext.completeTransition(finished)}} else {// 消失动画UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {fromView.alpha = 0.0fromView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)}) { finished infromView.removeFromSuperview()transitionContext.completeTransition(finished)}}}
}// 在视图控制器中设置自定义转场代理
class MyViewController: UIViewController, UIViewControllerTransitioningDelegate {override func viewDidLoad() {super.viewDidLoad()// 设置转场代理modalPresentationStyle = .customtransitioningDelegate = self}func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {return CustomModalTransition()}func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {return CustomModalTransition()}
}

十一、生命周期中的数据传递

在视图控制器生命周期中,数据传递是常见需求,不同阶段有不同的数据传递方式。

11.1 初始化时的数据传递

在创建视图控制器时传递数据:

// 初始化时传递数据示例
class DetailViewController: UIViewController {var item: Item?  // 要显示的数据override func viewDidLoad() {super.viewDidLoad()// 使用传递的数据配置界面if let item = item {title = item.nameconfigureView(with: item)}}
}// 在源视图控制器中传递数据
func showDetail(for item: Item) {let detailVC = DetailViewController(nibName: nil, bundle: nil)detailVC.item = itemnavigationController?.pushViewController(detailVC, animated: true)
}

11.2 反向数据传递

从子视图控制器向父视图控制器传递数据通常使用代理模式:

// 定义代理协议
protocol DataEntryDelegate: AnyObject {func didEnterData(_ data: String)
}// 子视图控制器
class DataEntryViewController: UIViewController {weak var delegate: DataEntryDelegate?var enteredData: String?@IBAction func saveButtonTapped(_ sender: UIButton) {// 保存数据并通知代理if let data = enteredData {delegate?.didEnterData(data)}navigationController?.popViewController(animated: true)}
}// 父视图控制器实现代理
class ParentViewController: UIViewController, DataEntryDelegate {func didEnterData(_ data: String) {// 处理接收到的数据self.data = dataupdateUI()}func showDataEntry() {let dataEntryVC = DataEntryViewController(nibName: nil, bundle: nil)dataEntryVC.delegate = selfnavigationController?.pushViewController(dataEntryVC, animated: true)}
}

11.3 使用闭包进行数据传递

另一种常见的数据传递方式是使用闭包:

// 使用闭包传递数据的视图控制器
class EditViewController: UIViewController {var onSave: ((String) -> Void)?var textToEdit: String?@IBAction func saveButtonTapped(_ sender: UIButton) {if let text = textToEdit, let onSave = onSave {onSave(text)}dismiss(animated: true)}
}// 在源视图控制器中使用闭包
func showEditScreen() {let editVC = EditViewController(nibName: nil, bundle: nil)editVC.textToEdit = currentTexteditVC.onSave = { [weak self] newText inself?.currentText = newTextself?.updateUI()}present(editVC, animated: true)
}

11.4 使用通知中心进行数据传递

对于跨层级的数据传递,可以使用通知中心:

// 定义通知名称
extension Notification.Name {static let dataUpdated = Notification.Name("DataUpdatedNotification")
}// 发布通知的视图控制器
class DataSourceViewController: UIViewController {func updateData() {// 更新数据let newData = fetchUpdatedData()// 发布通知NotificationCenter.default.post(name: .dataUpdated,object: self,userInfo: ["data": newData])}
}// 接收通知的视图控制器
class DataConsumerViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 注册通知NotificationCenter.default.addObserver(self,selector: #selector(handleDataUpdate(_:)),name: .dataUpdated,object: nil)}@objc func handleDataUpdate(_ notification: Notification) {if let data = notification.userInfo?["data"] as? Data {// 处理更新的数据updateUI(with: data)}}deinit {// 移除观察者NotificationCenter.default.removeObserver(self)}
}

十二、生命周期中的性能优化

在视图控制器的生命周期中进行性能优化是提高应用质量的关键。

12.1 延迟加载策略

通过懒加载避免不必要的资源初始化:

// 懒加载示例
class MyViewController: UIViewController {// 懒加载的视图组件private lazy var tableView: UITableView = {let tableView = UITableView()tableView.delegate = selftableView.dataSource = selftableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")return tableView}()// 懒加载的数据private lazy var data: [String] = {return loadDataFromDisk()}()override func viewDidLoad() {super.viewDidLoad()// 添加懒加载的视图view.addSubview(tableView)}override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 只在需要时访问数据if data.isEmpty {reloadData()}}
}

12.2 内存使用优化

在适当的时机释放资源:

// 内存优化示例
class MemoryIntensiveViewController: UIViewController {private var largeImage: UIImage?override func viewDidLoad() {super.viewDidLoad()// 加载大图片largeImage = loadLargeImage()imageView.image = largeImage}override func viewDidDisappear(_ animated: Bool) {super.viewDidDisappear(animated)// 视图不可见时释放大内存资源if !isBeingPresented {largeImage = nilimageView.image = nil}}override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()// 收到内存警告时释放可选资源if !_isVisible {largeImage = nilimageView.image = nil}}
}

12.3 渲染性能优化

优化视图渲染性能:

// 渲染性能优化示例
class PerformanceViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 优化视图配置setupViews()}private func setupViews() {// 使用不透明视图提高渲染效率view.isOpaque = true// 避免图层混合imageView.layer.shouldRasterize = false// 使用正确的内容模式imageView.contentMode = .scaleAspectFillimageView.clipsToBounds = true// 避免在主线程执行耗时操作DispatchQueue.global().async {let processedImage = self.processImage()DispatchQueue.main.async {self.imageView.image = processedImage}}}
}

12.4 响应式设计优化

确保视图控制器在各种情况下都能快速响应:

// 响应式设计优化示例
class ResponsiveViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 使用后台队列处理数据loadDataAsync()}private func loadDataAsync() {DispatchQueue.global().async {// 模拟耗时的数据加载let data = self.fetchDataFromServer()DispatchQueue.main.async {// 更新UI必须在主线程self.tableView.reloadData()}}}// 使用异步绘制override func viewWillLayoutSubviews() {super.viewWillLayoutSubviews()// 使用CATransaction确保布局更新在一次绘制周期内完成CATransaction.begin()CATransaction.setDisableActions(true)// 更新布局updateLayout()CATransaction.commit()}
}

十三、与SwiftUI的集成

随着SwiftUI的引入,UIViewController与SwiftUI的集成方式也成为生命周期管理的一部分。

13.1 UIHostingController的使用

UIHostingController是UIViewController的子类,用于承载SwiftUI视图:

// 使用UIHostingController的基本用法
class SwiftUIIntegrationViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 创建SwiftUI视图let swiftUIView = Text("Hello from SwiftUI!").padding()// 使用UIHostingController包装SwiftUI视图let hostingController = UIHostingController(rootView: swiftUIView)// 将UIHostingController添加为子控制器addChild(hostingController)view.addSubview(hostingController.view)hostingController.didMove(toParent: self)// 设置约束hostingController.view.frame = view.bounds}
}

13.2 从SwiftUI中呈现UIViewController

在SwiftUI中呈现UIViewController需要使用UIViewControllerRepresentable:

// 在SwiftUI中呈现UIViewController
struct ViewControllerPresenter: UIViewControllerRepresentable {func makeUIViewController(context: Context) -> UIViewController {// 创建要呈现的UIViewControllerlet viewController = MyUIViewController()return viewController}func updateUIViewController(_ uiViewController: UIViewController, context: Context) {// 更新视图控制器状态if let myVC = uiViewController as? MyUIViewController {myVC.configure(with: context.coordinator.data)}}func makeCoordinator() -> Coordinator {Coordinator(self)}class Coordinator: NSObject {var parent: ViewControllerPresentervar data: String = ""init(_ parent: ViewControllerPresenter) {self.parent = parent}}
}

13.3 混合编程的生命周期管理

当UIKit和SwiftUI混合使用时,需要特别注意生命周期管理:

// 混合编程的生命周期管理示例
class MixedProgrammingViewController: UIViewController {private var hostingController: UIHostingController<AnyView>?override func viewDidLoad() {super.viewDidLoad()// 创建SwiftUI视图let swiftUIView = AnyView(VStack {Text("UIKit & SwiftUI Integration")Button("Show UIKit View") {self.showUIKitView()}})// 创建UIHostingControllerhostingController = UIHostingController(rootView: swiftUIView)if let hostingController = hostingController {// 添加为子控制器addChild(hostingController)view.addSubview(hostingController.view)hostingController.didMove(toParent: self)// 设置约束hostingController.view.frame = view.bounds}}private func showUIKitView() {// 从SwiftUI触发UIKit视图呈现let detailVC = DetailViewController(nibName: nil, bundle: nil)navigationController?.pushViewController(detailVC, animated: true)}override func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 清理子控制器hostingController?.willMove(toParent: nil)hostingController?.view.removeFromSuperview()hostingController?.removeFromParent()hostingController = nil}
}

十四、单元测试与生命周期验证

对视图控制器生命周期进行单元测试是确保代码质量的重要环节。

14.1 测试生命周期方法调用

使用XCTest测试生命周期方法是否按预期调用:

// 测试生命周期方法调用
class ViewControllerLifecycleTests: XCTestCase {func testViewDidLoadCalled() {let viewController = MyViewController()let expectation = self.expectation(description: "viewDidLoad should be called")// 模拟视图加载_ = viewController.view// 验证viewDidLoad是否被调用XCTAssertTrue(viewController.didLoadView, "viewDidLoad should be called when accessing view")// 标记期望已满足expectation.fulfill()// 等待期望被满足waitForExpectations(timeout: 1.0)}func testViewWillAppearCalled() {let viewController = MyViewController()let expectation = self.expectation(description: "viewWillAppear should be called")// 模拟视图控制器被添加到导航控制器let navigationController = UINavigationController(rootViewController: viewController)// 呈现导航控制器let window = UIWindow(frame: UIScreen.main.bounds)window.rootViewController = navigationControllerwindow.makeKeyAndVisible()// 验证viewWillAppear是否被调用XCTAssertTrue(viewController.willAppearCalled, "viewWillAppear should be called when view appears")expectation.fulfill()waitForExpectations(timeout: 1.0)}
}// 为测试目的扩展视图控制器
extension MyViewController {var didLoadView: Bool = falsevar willAppearCalled: Bool = falseoverride func viewDidLoad() {super.viewDidLoad()didLoadView = true}override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)willAppearCalled = true}
}

14.2 测试数据传递

验证数据是否正确传递:

// 测试数据传递
class DataPassingTests: XCTestCase {func testDataPassedOnInitialization() {let item = Item(name: "Test Item", description: "This is a test item")let detailVC = DetailViewController(item: item)// 模拟视图加载_ = detailVC.view// 验证数据是否正确设置XCTAssertEqual(detailVC.item?.name, "Test Item", "Item name should be set correctly")XCTAssertEqual(detailVC.item?.description, "This is a test item", "Item description should be set correctly")}func testDelegateDataPassing() {let dataEntryVC = DataEntryViewController()let mockDelegate = MockDataEntryDelegate()dataEntryVC.delegate = mockDelegatedataEntryVC.enteredData = "Test Data"// 模拟保存按钮点击dataEntryVC.saveButtonTapped(UIButton())// 验证代理方法是否被调用XCTAssertTrue(mockDelegate.didEnterDataCalled, "Delegate method should be called")XCTAssertEqual(mockDelegate.receivedData, "Test Data", "Data should be passed correctly")}
}// 模拟代理
class MockDataEntryDelegate: DataEntryDelegate {var didEnterDataCalled = falsevar receivedData: String?func didEnterData(_ data: String) {didEnterDataCalled = truereceivedData = data}
}

14.3 测试内存管理

验证视图控制器在适当的时候释放资源:

// 测试内存管理
class MemoryManagementTests: XCTestCase {func testViewUnloadOnMemoryWarning() {let viewController = MemoryIntensiveViewController()// 模拟视图加载_ = viewController.view// 验证视图已加载XCTAssertNotNil(viewController.view, "View should be loaded")XCTAssertNotNil(viewController.largeImage, "Large image should be loaded")// 模拟内存警告viewController.didReceiveMemoryWarning()// 验证视图是否被卸载if !viewController.isVisible {XCTAssertNil(viewController.largeImage, "Large image should be released on memory warning when view is invisible")}}func testViewControllerDeallocation() {var viewController: MyViewController? = MyViewController()let expectation = self.expectation(description: "ViewController should be deallocated")// 设置弱引用用于检测对象释放weak var weakViewController = viewController// 释放强引用viewController = nil// 验证对象是否被释放XCTAssertNil(weakViewController, "ViewController should be deallocated")expectation.fulfill()waitForExpectations(timeout: 1.0)}
}

十五、常见问题与解决方案

在视图控制器生命周期管理中,开发者经常会遇到一些典型问题。

15.1 视图未正确加载

问题描述:视图控制器的viewDidLoad方法未被调用,或视图未显示。

可能原因

  • nib名称或bundle设置错误
  • 未正确初始化视图控制器
  • 视图层次结构问题

解决方案

// 确保正确初始化视图控制器
let viewController = MyViewController(nibName: "MyView", bundle: nil)// 或使用Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "MyViewController")// 检查视图是否正确添加到视图层次结构
addChild(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParent: self)

15.2 内存泄漏

问题描述:视图控制器在不再使用时未被释放,导致内存泄漏。

可能原因

  • 强引用循环(Retain Cycle)
  • 未正确移除通知观察者
  • 长生命周期对象持有对视图控制器的强引用

解决方案

// 使用弱引用避免循环引用
class MyViewController: UIViewController {var completionHandler: (() -> Void)?override func viewDidLoad() {super.viewDidLoad()// 使用弱引用捕获selfcompletionHandler = { [weak self] inself?.doSomething()}}deinit {// 移除通知观察者NotificationCenter.default.removeObserver(self)}
}

15.3 动画不流畅

问题描述:视图过渡或动画效果不流畅,出现卡顿。

可能原因

  • 主线程执行耗时操作
  • 视图层次结构过于复杂
  • 未正确设置视图属性

解决方案

// 在后台线程执行耗时操作
func loadData() {DispatchQueue.global().async {// 执行耗时操作let data = self.fetchData()DispatchQueue.main.async {// 更新UIself.tableView.reloadData()}}
}// 优化视图属性
override func viewDidLoad() {super.viewDidLoad()// 使用不透明视图提高性能view.isOpaque = true// 避免图层混合imageView.layer.shouldRasterize = false
}

15.4 数据传递失败

问题描述:在视图控制器之间传递的数据未正确接收。

可能原因

  • 数据传递时机不正确
  • 代理或闭包未正确设置
  • 数据类型不匹配

解决方案

// 确保在正确的时机传递数据
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {if let destination = segue.destination as? DetailViewController,let selectedItem = sender as? Item {// 传递数据destination.item = selectedItem}
}// 验证代理设置
class MyViewController: UIViewController, DataEntryDelegate {override func viewDidLoad() {super.viewDidLoad()let dataEntryVC = DataEntryViewController()dataEntryVC.delegate = self  // 确保设置代理}func didEnterData(_ data: String) {// 处理接收到的数据}
}

15.5 旋转后布局错乱

问题描述:设备旋转后,视图布局出现异常。

可能原因

  • 未正确实现旋转相关方法
  • 约束设置不正确
  • 手动布局代码未更新

解决方案

// 实现旋转相关方法
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {super.viewWillTransition(to: size, with: coordinator)coordinator.animate(alongsideTransition: { context in// 更新布局self.updateLayout(for: size)}, completion: nil)
}// 使用自动布局而非手动设置frame
func setupViews() {// 使用约束而非手动frameview.addSubview(myView)myView.translatesAutoresizingMaskIntoConstraints = falseNSLayoutConstraint.activate([myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),myView.leadingAnchor.constraint(equalTo: view.leadingAnchor),myView.trailingAnchor.constraint(equalTo: view.trailingAnchor),myView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)])
}

十六、高级话题与最佳实践

在掌握了视图控制器生命周期的基础知识后,了解一些高级话题和最佳实践可以帮助开发者写出更专业的代码。

16.1 自定义容器视图控制器

创建自定义容器视图控制器需要仔细管理子控制器的生命周期:

// 自定义容器视图控制器示例
class CustomContainerViewController: UIViewController {private var currentChild: UIViewController?override func viewDidLoad() {super.viewDidLoad()// 初始加载子控制器loadInitialChild()}private func loadInitialChild() {let child = createChildViewController()// 添加子控制器addChild(child)view.addSubview(child.view)child.didMove(toParent: self)// 设置子视图的约束configureChildViewConstraints(child.view)currentChild = child}func switchToChild(_ child: UIViewController) {// 移除当前子控制器if let currentChild = currentChild {currentChild.willMove(toParent: nil)currentChild.view.removeFromSuperview()currentChild.removeFromParent()}// 添加新的子控制器addChild(child)view.addSubview(child.view)child.didMove(toParent: self)// 设置约束configureChildViewConstraints(child.view)currentChild = child}private func configureChildViewConstraints(_ childView: UIView) {childView.translatesAutoresizingMaskIntoConstraints = falseNSLayoutConstraint.activate([childView.topAnchor.constraint(equalTo: view.topAnchor),childView.leadingAnchor.constraint(equalTo: view.leadingAnchor),childView.trailingAnchor.constraint(equalTo: view.trailingAnchor),childView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])}
}

16.2 与MVVM架构结合

将视图控制器生命周期与MVVM架构模式结合:

// MVVM架构中的视图控制器
class UserListViewController: UIViewController {private let tableView = UITableView()private let viewModel = UserListViewModel()override func viewDidLoad() {super.viewDidLoad()// 设置UIsetupTableView()// 绑定ViewModelbindViewModel()// 加载数据viewModel.loadUsers()}private func setupTableView() {view.addSubview(tableView)tableView.frame = view.boundstableView.delegate = selftableView.dataSource = selftableView.register(UserCell.self, forCellReuseIdentifier: "UserCell")}private func bindViewModel() {// 监听数据变化viewModel.users.bind { [weak self] _ inself?.tableView.reloadData()}// 监听错误viewModel.error.bind { [weak self] error inif let error = error {self?.showError(error)}}}override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 更新导航栏navigationController?.navigationBar.prefersLargeTitles = truetitle = "Users"}
}// 对应的ViewModel
class UserListViewModel {let users = Observable<[User]>([])let error = Observable<String?>(nil)func loadUsers() {// 模拟网络请求DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) { [weak self] in// 成功获取数据let mockUsers = [User(id: 1, name: "John"), User(id: 2, name: "Jane")]self?.users.value = mockUsers}}
}

16.3 处理多窗口环境

在多窗口环境中管理视图控制器生命周期:

// 支持多窗口的应用实现
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {// 返回场景配置return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)}
}// 窗口场景代理
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 }// 创建窗口window = UIWindow(windowScene: windowScene)// 创建根视图控制器let navigationController = UINavigationController(rootViewController: HomeViewController())// 设置窗口根控制器window?.rootViewController = navigationControllerwindow?.makeKeyAndVisible()}func sceneDidDisconnect(_ scene: UIScene) {// 场景断开连接时的清理工作}func sceneDidBecomeActive(_ scene: UIScene) {// 场景变为活跃状态}func sceneWillResignActive(_ scene: UIScene) {// 场景即将变为非活跃状态}func sceneWillEnterForeground(_ scene: UIScene) {// 场景即将进入前台}func sceneDidEnterBackground(_ scene: UIScene) {// 场景进入后台}
}

16.4 性能监控与优化

监控视图控制器性能并进行优化:

// 性能监控工具
class PerformanceMonitor {private var displayLink: CADisplayLink?private var lastTimestamp: CFTimeInterval = 0private var frameCount: Int = 0func startMonitoring() {displayLink = CADisplayLink(target: self, selector: #selector(trackFPS))displayLink?.add(to: .main, forMode: .common)}func stopMonitoring() {displayLink?.invalidate()displayLink = nil}@objc private func trackFPS(displayLink: CADisplayLink) {if lastTimestamp == 0 {lastTimestamp = displayLink.timestampreturn}frameCount += 1let delta = displayLink.timestamp - lastTimestampif delta >= 1.0 {let fps = Double(frameCount) / deltaprint("当前FPS: \(fps)")frameCount = 0lastTimestamp = displayLink.timestamp}}
}// 在视图控制器中使用性能监控
class PerformanceCriticalViewController: UIViewController {private let monitor = PerformanceMonitor()override func viewDidLoad() {super.viewDidLoad()// 开始监控性能monitor.startMonitoring()}override func viewWillDisappear(_ animated: Bool) {super.viewWillDisappear(animated)// 停止监控monitor.stopMonitoring()}
}

16.5 测试驱动开发

使用测试驱动开发(TDD)方法开发视图控制器:

// TDD开发视图控制器示例
// 首先编写测试
class LoginViewControllerTests: XCTestCase {func testLoginSuccess() {let viewModel = MockLoginViewModel()let viewController = LoginViewController(viewModel: viewModel)// 设置用户名和密码viewController.username = "test@example.com"viewController.password = "password"// 模拟登录按钮点击viewController.loginButtonTapped(nil)// 验证ViewModel的login方法是否被调用XCTAssertTrue(viewModel.loginCalled, "Login method should be called")XCTAssertEqual(viewModel.username, "test@example.com", "Username should be passed to viewModel")XCTAssertEqual(viewModel.password, "password", "Password should be passed to viewModel")}
}// 然后实现视图控制器
class LoginViewController: UIViewController {var username: String = ""var password: String = ""private let viewModel: LoginViewModelProtocolinit(viewModel: LoginViewModelProtocol) {self.viewModel = viewModelsuper.init(nibName: nil, bundle: nil)}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}@IBAction func loginButtonTapped(_ sender: UIButton?) {viewModel.login(username: username, password: password)}
}// 协议定义
protocol LoginViewModelProtocol {var loginCalled: Bool { get }var username: String? { get }var password: String? { get }func login(username: String, password: String)
}// 模拟ViewModel
class MockLoginViewModel: LoginViewModelProtocol {var loginCalled = falsevar username: String?var password: String?func login(username: String, password: String) {loginCalled = trueself.username = usernameself.password = password}
}

通过深入理解UIViewController的生命周期及其源码实现,开发者可以更好地控制视图的加载、布局和内存管理,从而构建出更加高效、稳定和用户友好的iOS应用。掌握这些知识不仅有助于解决日常开发中的问题,还能为高级应用架构设计打下坚实的基础。