如何在iOS中使用UIViewController的生命周期方法?

一、核心原则(先记牢)

  1. 所有生命周期方法都要先调用父类的实现 (比如[super viewDidLoad]),且通常放在方法第一行。
  2. 不要手动调用生命周期方法(比如不要自己写[self viewDidAppear:YES]),由系统自动触发。
  3. 按「初始化→视图加载→显示→布局→消失→销毁」的逻辑分配代码,避免功能混乱。

二、分场景使用(附完整示例)

下面是一个贴近实际开发的UIViewController示例,标注了每个生命周期方法的典型用法:

objective-c

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

@interface DemoViewController : UIViewController
// 定义需要管理的资源(定时器、监听、网络请求等)
@property (nonatomic, strong) NSTimer *updateTimer;
@property (nonatomic, strong) NSURLSessionDataTask *dataTask;
@end

@implementation DemoViewController

// 1. 初始化:仅做数据/属性初始化,不创建UI
- (instancetype)initWithTitle:(NSString *)title {
    self = [super init];
    if (self) {
        // 初始化业务数据、配置项
        self.title = title ?: @"默认标题";
        NSLog(@"1. 初始化:仅初始化数据,不创建UI");
    }
    return self;
}

// 2. 视图加载完成:一次性创建UI、绑定数据(仅调用一次)
- (void)viewDidLoad {
    [super viewDidLoad]; // 必须先调父类
    NSLog(@"2. 视图加载完成:创建UI、初始化一次性资源");
    
    // ✅ 典型用法1:设置视图基础样式
    self.view.backgroundColor = [UIColor whiteColor];
    
    // ✅ 典型用法2:创建UI控件并添加到视图
    UIButton *testBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    testBtn.frame = CGRectMake(100, 200, 200, 50);
    [testBtn setTitle:self.title forState:UIControlStateNormal];
    [testBtn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:testBtn];
    
    // ✅ 典型用法3:初始化网络请求(仅发起一次的请求,比如获取页面基础数据)
    [self loadBaseData];
}

// 3. 视图即将显示:每次显示都要做的操作(比如刷新数据、开启监听)
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated]; // 必须先调父类
    NSLog(@"3. 视图即将显示:刷新数据、开启定时器/监听");
    
    // ✅ 典型用法1:刷新页面数据(比如从其他页面返回后更新)
    [self refreshPageData];
    
    // ✅ 典型用法2:开启定时器/通知监听
    self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:nil];
    
    // ✅ 典型用法3:设置导航栏/状态栏样式
    self.navigationController.navigationBar.tintColor = [UIColor blueColor];
}

// 4. 视图布局:调整控件位置(适配屏幕旋转、尺寸变化)
- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"4. 视图即将布局:调整控件frame(可选)");
    // ✅ 典型用法:手动调整控件布局(比如适配不同屏幕尺寸)
    UIButton *testBtn = self.view.subviews.firstObject;
    testBtn.centerXAnchor.constraintEqualToAnchor:self.view.centerXAnchor.active = YES; // 自动布局示例
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSLog(@"5. 视图布局完成:获取控件最终尺寸");
    // ✅ 典型用法:获取控件最终的frame/size(比如ScrollView的contentSize)
    UIButton *testBtn = self.view.subviews.firstObject;
    NSLog(@"按钮最终尺寸:%@", NSStringFromCGRect(testBtn.frame));
}

// 5. 视图已经显示:执行动画、请求权限(需要视图可见时操作)
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"6. 视图已经显示:执行动画、请求权限");
    
    // ✅ 典型用法1:执行视图动画
    UIButton *testBtn = self.view.subviews.firstObject;
    [UIView animateWithDuration:0.5 animations:^{
        testBtn.transform = CGAffineTransformMakeScale(1.1, 1.1);
    }];
    
    // ✅ 典型用法2:请求权限(比如定位、相册,需要视图可见时弹框)
    [self requestPhotoPermission];
}

// 6. 视图即将消失:清理临时资源(关闭定时器、移除监听)
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSLog(@"7. 视图即将消失:关闭定时器、移除监听");
    
    // ✅ 典型用法1:停止定时器
    [self.updateTimer invalidate];
    self.updateTimer = nil; // 置空避免野指针
    
    // ✅ 典型用法2:移除通知监听
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // ✅ 典型用法3:取消未完成的网络请求
    [self.dataTask cancel];
    self.dataTask = nil;
    
    // ✅ 典型用法4:隐藏键盘
    [self.view endEditing:YES];
}

// 7. 视图已经消失:释放非必要资源(比如缓存、临时文件)
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    NSLog(@"8. 视图已经消失:释放临时资源");
    // ✅ 典型用法:清理内存缓存
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
}

// 8. 内存警告:释放非核心资源(避免APP崩溃)
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    NSLog(@"9. 内存警告:释放非必要资源");
    
    // ✅ 典型用法:释放图片缓存、非当前显示的数据
    [[SDImageCache sharedImageCache] clearMemory]; // 第三方图片缓存示例
    self.dataTask = nil; // 取消网络请求
}

// 9. 销毁:最终清理(移除所有强引用、避免内存泄漏)
- (void)dealloc {
    NSLog(@"10. 控制器销毁:最终清理资源");
    // ✅ 典型用法:最后兜底清理(比如确认定时器/监听已移除)
    [self.updateTimer invalidate];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

// --------------- 辅助方法(业务逻辑)---------------
- (void)loadBaseData {
    // 初始化页面基础数据(仅调用一次)
    self.dataTask = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://example.com/baseData"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 处理数据...
    }];
    [self.dataTask resume];
}

- (void)refreshPageData {
    // 每次显示页面都刷新数据
    NSLog(@"刷新页面数据");
}

- (void)timerAction {
    // 定时器回调
    NSLog(@"定时器执行:更新页面状态");
}

- (void)handleNotification:(NSNotification *)notification {
    // 通知监听回调
}

- (void)requestPhotoPermission {
    // 请求相册权限
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        // 处理权限结果...
    }];
}

- (void)btnClick {
    NSLog(@"按钮点击");
}

@end

三、常见错误(避坑指南)

  1. ❌ 不在viewDidLoad里创建 UI,反而在init里创建:init阶段self.view还未加载,创建的 UI 无法添加到视图,会导致控件不显示。
  2. ❌ 忘记调用super的生命周期方法:比如- (void)viewDidLoad { /* 没写[super viewDidLoad] */ },会导致系统默认逻辑失效(比如视图无法正常加载)。
  3. ❌ 在viewWillAppear:里创建 UI:每次页面显示都会重复创建控件,导致内存泄漏、控件叠加。
  4. ❌ 没有在viewWillDisappear:里停止定时器 / 移除监听:控制器销毁后定时器仍在运行,会导致野指针崩溃(经典内存泄漏场景)。

四、Swift 版本简化示例(适配 Swift 开发者)

如果用 Swift 开发,核心逻辑一致,语法稍有不同:

swift

复制代码
import UIKit

class DemoViewController: UIViewController {
    var updateTimer: Timer?
    var dataTask: URLSessionDataTask?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        self.title = "默认标题"
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // 创建UI...
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updateTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        updateTimer?.invalidate()
        updateTimer = nil
        dataTask?.cancel()
        dataTask = nil
    }

    @objc func timerAction() {
        print("定时器执行")
    }

    deinit {
        print("控制器销毁")
    }
}

总结

  1. 分工明确viewDidLoad做「一次性初始化(UI / 基础数据)」,viewWillAppear:做「每次显示都要刷新的操作」,viewWillDisappear:做「资源清理」。
  2. 资源闭环:开启的资源(定时器、监听、网络请求)必须在对应阶段关闭,避免内存泄漏。
  3. 遵循规范:必调父类方法、不手动触发生命周期方法,是避免崩溃的核心。
相关推荐
2501_915106329 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
2501_915106329 小时前
使用 Sniffmaster TCP 抓包和 Wireshark 网络分析
网络协议·tcp/ip·ios·小程序·uni-app·wireshark·iphone
熊猫钓鱼>_>9 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
徐同保1 天前
通过ip访问nginx的服务时,被第一个server重定向了,通过设置default_server解决这个问题
ios·iphone
皮卡车厘子1 天前
Mac 挂载目录
macos
良逍Ai出海1 天前
在 Windows & macOS 上安装 Claude Code,并使用第三方 Key 的完整教程
windows·macos
热爱生活的五柒1 天前
linux/mac/wsl如何使用claude code,并配置免费的硅基流动API?(官方的需要付费订阅)
linux·运维·macos
2501_915918411 天前
在 iOS 环境下查看 App 详细信息与文件目录
android·ios·小程序·https·uni-app·iphone·webview
胖胖大王叫我来巡山1 天前
mac本地安装DataEase桌面版
macos
奋斗者1号1 天前
OpenClaw 部署方式对比:云端、WSL、Mac 本机、Ubuntu 虚拟机(2026年2月最新主流实践)
linux·ubuntu·macos