核心概念
本质:一组较为稳定的事件回调。
从VC的生命周期谈起,并扩展讲讲部分相关的API。
UIViewController
1. 初始化阶段
- 
+initialize : 类的初始化方法 - 时机:仅 oc,且首次初始化时才会调用。 
- 
-init: 实例的初始化方法 - 如果是从 xib/storyboard 来的,调用会变成:
- -initWithCoder : 在 nib 或 storyboard 解码时调用(对象刚被创建,未连接 IBOutlet)。
- -awakeFromNib : 所有子对象实例化后,并且IBOutlet都连接好后调用。
 
- -initWithCoder : 在 nib 或 storyboard 解码时调用(对象刚被创建,未连接 
 
- 如果是从 xib/storyboard 来的,调用会变成:
- 
-loadView : 创建 vc 的 view - 时机:访问 vc 的 view 且 view 为空时调用 - [super loadView] 默认实现 :
- 设置了 nibName,通过 name 查找对应 nib:
- 有资源,则加载对应 nib。
- 没资源,会按照类名匹配主 bundle 下的同名 nib。
 
- 未设置 nibName,创建一个空白 view。
 
- 设置了 nibName,通过 name 查找对应 nib:
 
- [super loadView] 默认实现 :
2. 生命周期(相关流程)
stateDiagram-v2
    [*] --> viewDidLoad: vc 的 view 创建完成后调用
    viewDidLoad --> viewWillAppear: 视图即将可见
    viewWillAppear --> viewIsAppearing: 视图的几何尺寸、safe area、trait 等环境已确认
    viewIsAppearing --> updateViewConstraints: 约束更新,布局求解
    updateViewConstraints --> viewWillLayoutSubviews: 在本轮布局阶段开始前回调(即将布局子视图)
    viewWillLayoutSubviews --> viewDidLayoutSubviews: 在本轮布局阶段结束时回调
    viewDidLayoutSubviews --> updateViewConstraints: 循环次数系统决定,可能 0 次可能多次
    viewDidLayoutSubviews --> viewDidAppear: 过渡动画
    viewDidAppear --> viewWillDisappear: 视图即将不可见
    viewWillDisappear --> viewDidDisappear: 过渡动画
    viewDidDisappear --> [*]: 视图不可见
⚠️:Appear 阶段的回调顺序并不是固定的,也可能是:
stateDiagram-v2 [*] --> updateViewConstraints updateViewConstraints --> viewIsAppearing viewIsAppearing --> viewWillLayoutSubviews viewWillLayoutSubviews --> viewDidLayoutSubviews viewDidLayoutSubviews --> [*]
可以看出-updateViewConstraints和-viewIsAppearing的顺序不一定是固定的。
- 原因:
- 二者不构成先后必然关系;
- 他们分别由"外观转场调度"与"布局引擎调度"驱动,是UIKit中两条协同的流程;- 外观转场调度 :外观/生命周期由容器控制器(如导航)通过begin/endAppearanceTransition等驱动,负责"让谁消失/出现"的调度。- 触发外观回调 :viewWillAppear → viewIsAppearing → viewDidAppear、viewWillDisappear → viewDidDisappear。
 
- 触发外观回调 :
- 布局引擎调度 :约束/布局由Auto Layout 引擎在布局阶段驱动,负责"计算 frame/安全区/约束应用"的调度。- 触发布局回调 :updateViewConstraints → viewWillLayoutSubviews → viewDidLayoutSubviews。
 
- 触发布局回调 :
 
- 外观转场调度 :外观/生命周期由
- 他们在主线程的同一个RunLoop上交替工作:- 外观转场会引发几何/安全区变化,从而"标记"需要布局。
- 布局完成又为转场呈现提供正确的 frame。
 
 
3. 其他(不太常用)
- 销毁
- -dealloc
 
- 内存告警
- -didReceiveMemoryWarning:内存不足时,被 iOS 调用
- -viewDidUnload:已弃用(iOS 3 ~ 6)
 
- 容器关系
- -willMoveToParentViewController
- -didMoveToParentViewController
 
- 环境特征/尺寸变化
- viewWillTransition(to:with:):旋转/分屏、- pageSheet等拉动导致控制器视图 size 变化的场景。
- traitCollectionDidChange(_:):布局方向变化(阿拉伯语 LTR -> RTL)、旋转/分屏等。
 
UIView(其实没有生命周期的概念,只是一些常用的事件回调)
1. 初始化
同 VC,只是没有 -loadView 而已。
2. 常用
- 层级与窗口
- -willMoveToSuperview->- -didMoveToSuperview
- -willMoveToWindow->- -didMoveToWindow
 
- 约束与布局
- -setNeedsLayout:标记需要布局, 等待下次- RunLoop执行布局
- layoutIfNeeded:若被标记为需要布局,则"立刻在当前- RunLoop执行一次布局"。
- layoutSubviews:布局过程中的回调,不能手动直接调。
 
什么操作会标记"需要布局"呢?
- 显示触发
- 调用 -setNeedsLayout方法。
- 调用 -setNeedsUpdateConstraints或修改约束后
 
- 调用 
- 几何与层级变更(UIKit 内部会标记)
- 修改 frame/bounds/center/transform
- 父视图的 bounds/safe area变化
- 视图 首次加入窗口 或 窗口变化(-willMoveToWindow)
 
- 修改 
- Auto Layout 相关
- 改约束的 常量/优先级、启用/禁用
- 改组件的 抗压缩/抗拉伸 优先级
- translatesAutoresizingMaskIntoConstraints切换导致约束变化
 
- 改
- 内在尺寸(intrinsicContentSize)变化 -(视图"基于自身内容的天然尺寸",不依赖外部约束)
- 调用invalidateIntrinsicContentSize
- 改变内在尺寸的属性更新:text/font/numberOfLines等等。
 
- 调用
3. 其他(不太常用)
- 约束与布局
- -setNeedsUpdateConstraints->- -updateConstraints
 
- 环境变化
- traitCollectionDidChange
- tintColorDidChange
- safeAreaInsetsDidChange
- layoutMarginsDidChange
 
- 渲染
- setNeedsDisplay / setNeedsDisplay(_:)
- draw(_:)
 
VC 和 View 回调的交叉(切换 vc,创建加载 view 等)
回调顺序:
1. VC 的切换
            
            
              swift
              
              
            
          
          // VC(A) 切换到 VC(B)
1. B -loadView  
2. B -viewDidload  
  
3. A -viewWillDisappear  
  
4. B -viewWillAppear  
5. B -viewWillLayoutSubviews  
6. B -viewDidLayoutSubviews  
  
7. A -viewDidDisappear  
  
8. B -viewDidAppear2. VC 与 View 的交叉
            
            
              swift
              
              
            
          
          // 添加 viewB
1. VC - addSubview:viewB
2. viewB - willMoveToSuperview
3. viewB - didMoveToSuperview
// 出现 view
1. VC - viewWillAppear
2. viewB - willMoveToWindow
3. viewB - didMoveToWindow
4. VC - viewWillLayoutSubviews
5. VC - viewDidLayoutSubviews
6. viewB - layoutSubviews
7. VC - viewDidAppear疑问:
为什么子 view 的
-layoutSubviews打印在-viewDidLayoutSubviews之后?
-viewDidLayoutSubviews的字面含义不是子 view 都做完-layoutSubviews了`?
- 其实顺序是正确的,并不矛盾。-viewDidLayoutSubviews并不保证"所有子 view 的-layoutSubviews都已经执行完",它只是"VC根视图这一轮布局周期结束"的回调。子视图的第一次布局可能被推迟到下一次布局循环,因此会出现在 viewDidLayoutSubviews 之后。