如何在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. 遵循规范:必调父类方法、不手动触发生命周期方法,是避免崩溃的核心。
相关推荐
Benny的老巢16 分钟前
Mac上用XAMPP搭建局域网可访问的开发环境,让局域网内其他设备通过域名访问
nginx·macos·apache·xampp·php开发环境
2501_941881408 小时前
在墨西哥城复杂流量环境下构建高稳定性API网关的架构设计与实现实践分享
macos·golang·xcode
wadesir8 小时前
macOS Sequoia与macOS Tahoe全面对比:功能、性能与升级教程(新手入门指南)
macos
茅根竹蔗水__9 小时前
iOS应用(App)生命周期、视图控制器(UIViewController)生命周期和视图(UIView)生命周期
ios
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