iOS - UIViewController 生命周期

一、UIViewController 生命周期核心流程(以 iOS 13 + 为例)

先通过流程图直观理解整体顺序:

复制代码
graph TD
    A[初始化 init] --> B[加载视图 loadView]
    B --> C[视图加载完成 viewDidLoad]
    C --> D[视图将要出现 viewWillAppear:]
    D --> E[视图布局更新 viewWillLayoutSubviews]
    E --> F[视图布局完成 viewDidLayoutSubviews]
    F --> G[视图已经出现 viewDidAppear:]
    
    %% 页面消失流程
    G --> H[视图将要消失 viewWillDisappear:]
    H --> I[视图已经消失 viewDidDisappear:]
    
    %% 内存警告/销毁流程
    I --> J{是否销毁?}
    J -->|是| K[视图卸载 viewDidUnload - 已废弃]
    J -->|是| L[销毁 dealloc]
    J -->|否| D
    
    %% 内存警告分支
    C --> M[收到内存警告 didReceiveMemoryWarning]
    M --> N[释放非必要资源]
    N --> D

初始化 init

加载视图 loadView

视图加载完成 viewDidLoad

视图将要出现 viewWillAppear:

视图布局更新 viewWillLayoutSubviews

视图布局完成 viewDidLayoutSubviews

视图已经出现 viewDidAppear:

视图将要消失 viewWillDisappear:

视图已经消失 viewDidDisappear:

是否销毁?

视图卸载 viewDidUnload - 已废弃

销毁 dealloc

收到内存警告 didReceiveMemoryWarning

释放非必要资源

二、核心方法详解(新手友好版)

方法名 作用 & 使用场景
init / initWithNibName:bundle: 控制器初始化,仅分配内存,不要在这里创建 UI 控件(视图还没加载),适合初始化数据模型。
loadView 系统自动调用,用于创建控制器的view(根视图)。⚠️ 新手注意:不要主动调用,也不要重写除非你要自定义根视图(比如用纯代码替代 xib/storyboard)。
viewDidLoad 视图加载完成后调用(仅调用一次)。✅ 核心场景:创建 UI 控件、设置控件属性、网络请求初始化、数据绑定(比如给 tableView 设数据源)。
viewWillAppear: 视图即将显示在屏幕上时调用(每次页面显示都会触发,比如从后台切回、跳转返回)。✅ 场景:刷新数据、设置导航栏 / 状态栏样式、开启定时器 / 监听。
viewWillLayoutSubviews 视图即将布局子控件时调用(比如屏幕旋转、view 大小变化)。✅ 场景:调整控件布局(比如手动计算 frame)。
viewDidLayoutSubviews 视图子控件布局完成后调用。✅ 场景:获取控件最终的 frame/size(比如 scrollView 的 contentSize)。
viewDidAppear: 视图已经显示在屏幕上时调用。✅ 场景:执行动画、开始播放视频 / 音频、请求定位权限。
viewWillDisappear: 视图即将从屏幕消失时调用。✅ 场景:关闭定时器、移除监听、保存数据、隐藏键盘。
viewDidDisappear: 视图已经消失时调用。✅ 场景:停止网络请求、释放临时资源。
didReceiveMemoryWarning 系统内存不足时调用。✅ 场景:释放缓存、图片、非必要的对象(注意不要释放正在使用的资源)。
dealloc 控制器销毁时调用(ARC 自动管理)。✅ 场景:最后清理资源(比如移除所有监听、释放自定义的强引用对象)。

三、简单代码示例

objective-c

复制代码
#import <UIKit/UIKit.h>

@interface TestViewController : UIViewController
@end

@implementation TestViewController

// 1. 初始化
- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"1. 控制器初始化 init");
        // 初始化数据模型
        self.title = @"测试页面";
    }
    return self;
}

// 2. 加载视图(一般不重写)
- (void)loadView {
    [super loadView];
    NSLog(@"2. 加载视图 loadView");
    // 若自定义根视图,可在这里设置:self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}

// 3. 视图加载完成(仅一次)
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"3. 视图加载完成 viewDidLoad");
    // 创建UI控件
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(100, 100, 200, 50);
    [btn setTitle:@"测试按钮" forState:UIControlStateNormal];
    [self.view addSubview:btn];
}

// 4. 视图即将显示
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"4. 视图即将显示 viewWillAppear:");
    // 刷新数据
    self.navigationController.navigationBarHidden = NO;
}

// 5. 视图即将布局子控件
- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"5. 视图即将布局子控件 viewWillLayoutSubviews");
}

// 6. 视图布局子控件完成
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSLog(@"6. 视图布局子控件完成 viewDidLayoutSubviews");
    // 获取控件最终尺寸
    NSLog(@"按钮宽度:%f", self.view.subviews.firstObject.frame.size.width);
}

// 7. 视图已经显示
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"7. 视图已经显示 viewDidAppear:");
    // 执行动画
    [UIView animateWithDuration:0.3 animations:^{
        self.view.subviews.firstObject.alpha = 1.0;
    }];
}

// 8. 视图即将消失
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSLog(@"8. 视图即将消失 viewWillDisappear:");
    // 关闭定时器/监听
}

// 9. 视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    NSLog(@"9. 视图已经消失 viewDidDisappear:");
}

// 10. 内存警告
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    NSLog(@"10. 收到内存警告 didReceiveMemoryWarning");
    // 释放缓存
}

// 11. 销毁
- (void)dealloc {
    NSLog(@"11. 控制器销毁 dealloc");
    // 最终清理
}

@end

运行输出顺序(首次进入页面):

plaintext

复制代码
1. 控制器初始化 init
2. 加载视图 loadView
3. 视图加载完成 viewDidLoad
4. 视图即将显示 viewWillAppear:
5. 视图即将布局子控件 viewWillLayoutSubviews
6. 视图布局子控件完成 viewDidLayoutSubviews
7. 视图已经显示 viewDidAppear:

返回上一页时输出

plaintext

复制代码
8. 视图即将消失 viewWillDisappear:
9. 视图已经消失 viewDidDisappear:
11. 控制器销毁 dealloc

总结

  1. 核心顺序initloadViewviewDidLoadviewWillAppear:viewWillLayoutSubviewsviewDidLayoutSubviewsviewDidAppear:viewWillDisappear:viewDidDisappear:dealloc
  2. 关键区分viewDidLoad 仅调用一次(适合初始化 UI),viewWillAppear:/viewDidAppear: 每次显示都调用(适合刷新数据 / 执行动画)。
  3. 内存管理didReceiveMemoryWarning 释放非必要资源,dealloc 做最终清理,避免内存泄漏。
相关推荐
FreeBuf_11 小时前
新型TCC绕过漏洞:macOS面临自动化攻击风险
macos·自动化·策略模式
Alice12 小时前
Remote control Mac ios
macos
huaiyanchen12 小时前
mac Navicat 下载及安装
macos
毛发浓密的女猴子12 小时前
SSE Connect 数据解析详解
ios
人生何处不修行13 小时前
iOS 自定义视频播放器实战:全屏旋转 + 画中画(PiP)+ 多页面切换不中断播放
macos·objective-c·cocoa
JAVA+C语言14 小时前
iOS App小组件(Widget)显示LottieFiles动画
ios
2501_9159214314 小时前
如何将 iOS 应用的 IPA 文件安装到手机进行测试
android·ios·智能手机·小程序·uni-app·iphone·webview
2501_9160088914 小时前
不连 Xcode,也能把 iPhone App 的实时日志看清楚
android·ios·小程序·https·uni-app·iphone·webview
小CC吃豆子14 小时前
uni-app 上架 iOS 时常见的审核被拒原因有哪些?
ios·uni-app