UIViewController 生命周期

84 阅读4分钟

UIViewController 的生命周期方法反映了控制器从创建到销毁的完整过程,这些方法按特定顺序调用,用于处理视图加载、布局、显示和销毁等逻辑。以下是主要生命周期方法的执行顺序及作用:

一、初始化阶段

  1. init 系列方法

    • 控制器的初始化方法(如 init(nibName:bundle:) 或自定义初始化方法),最早被调用。
    • 用于初始化数据、设置属性等(此时视图尚未创建)。
  2. loadView()

    • 负责创建控制器的根视图(view 属性)。
    • 若未使用 XIB/Storyboard,需重写此方法手动创建 view(如 self.view = UIView());若使用 XIB,则系统会自动加载。
    • 注意:不要直接调用此方法,由系统自动触发。

二、视图加载阶段

  1. viewDidLoad()

    • 视图(view)加载完成后调用(从 XIB 加载或 loadView 创建后)。
    • 主要用于初始化视图控件(如按钮、标签)、设置约束、绑定数据等。
    • 此时视图的 bounds 可能尚未确定(不适合布局计算)。

三、布局与显示阶段

  1. viewWillAppear(_ animated: Bool)

    • 视图即将显示在屏幕上时调用。
    • 可在此更新数据、调整状态栏样式、启动动画等。
  2. viewWillLayoutSubviews()

    • 即将计算子视图布局时调用(每次视图尺寸变化都会触发,如旋转屏幕、弹窗)。
    • 适合调整子视图布局相关的属性(如 frametransform)。
  3. viewDidLayoutSubviews()

    • 子视图布局计算完成后调用。
    • 此时可获取到准确的视图尺寸(bounds),适合进行依赖尺寸的布局调整(如手动计算位置)。
  4. viewDidAppear(_ animated: Bool)

    • 视图完全显示在屏幕上后调用。
    • 可在此启动需要可见性的任务(如开始播放视频、请求网络数据)。

四、视图消失阶段

  1. viewWillDisappear(_ animated: Bool)

    • 视图即将从屏幕消失时调用。
    • 可在此暂停动画、保存数据、隐藏键盘等。
  2. viewDidDisappear(_ animated: Bool)

    • 视图完全消失后调用。
    • 适合释放不再需要的资源(如取消网络请求、移除通知监听)。

五、内存警告与销毁阶段

  1. didReceiveMemoryWarning()

    • 系统内存不足时调用,用于释放非必要资源。
    • 可重写此方法,清理缓存、销毁不可见的视图等(如 self.view = nil,触发 loadView 重新加载)。
  2. deinit

    • 控制器销毁时调用,是生命周期的最后一步。
    • 用于释放所有资源(如移除定时器、解除代理引用,避免内存泄漏)。

完整执行顺序(简化)

plaintext

初始化(init) → loadView() → viewDidLoad()  
→ viewWillAppear() → viewWillLayoutSubviews() → viewDidLayoutSubviews()  
→ viewDidAppear()  
→ (视图可见,可能多次触发 layout 方法)  
→ viewWillDisappear() → viewDidDisappear()  
→ (内存警告时:didReceiveMemoryWarning())  
→ deinit(控制器销毁)

关键注意事项

  • 布局时机:涉及尺寸的计算应放在 viewDidLayoutSubviews() 中,而非 viewDidLoad()(此时尺寸可能不正确)。

  • 复用与销毁UINavigationController 或 UITabBarController 中的控制器,在切换时只会执行 viewWillAppear/viewDidDisappear 等方法,不会立即销毁(deinit 可能延迟调用)。

  • 避免重复初始化viewDidLoad 仅在视图首次加载时调用,重复显示时不会触发,需区分初始化逻辑和刷新逻辑。

理解这些方法的调用时机,有助于合理安排数据加载、UI 初始化和资源释放,写出更规范的控制器代码。

注意横竖屏切换

当设备横竖屏切换时,viewWillLayoutSubviews 和 viewDidLayoutSubviews 会被再次调用

原因如下:

  • 横竖屏切换会导致视图(UIView)的尺寸(bounds)发生变化(宽高互换)。

  • 系统为了重新计算和调整子视图的布局,会触发布局更新流程,其中就包括这两个方法:

    1. 首先调用 viewWillLayoutSubviews(即将开始重新布局子视图)
    2. 系统内部计算并更新子视图的位置和尺寸
    3. 然后调用 viewDidLayoutSubviews(子视图布局已完成)

实际应用场景

这两个方法是处理横竖屏布局适配的重要时机,例如:

swift

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    // 横竖屏切换后,在此获取最新的视图尺寸并调整布局
    if view.bounds.width > view.bounds.height {
        // 横屏布局逻辑
        horizontalLayout()
    } else {
        // 竖屏布局逻辑
        verticalLayout()
    }
}

注意

  • 除了横竖屏切换,其他导致视图尺寸变化的情况(如弹出键盘、父视图尺寸改变)也会触发这两个方法。

  • 重写时必须调用 super 方法(super.viewWillLayoutSubviews() / super.viewDidLayoutSubviews()),否则可能导致布局异常。

因此,这两个方法是处理动态布局变化的核心回调,尤其适合横竖屏适配场景。