当前位置: 首页 > news >正文

如何在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. 遵循规范:必调父类方法、不手动触发生命周期方法,是避免崩溃的核心。
http://www.cnnetsun.cn/news/442393.html

相关文章:

  • iOS - UIViewController 生命周期
  • Dify DOCX处理性能翻倍指南:99%的人都忽略的底层优化细节
  • .NET代码混淆终极指南:Obfuscar完整使用教程
  • VutronMusic:跨平台音乐播放器的智能管理体验
  • Obfuscar实战手册:5步打造坚不可摧的.NET代码防线
  • 数字永生争议:用IndexTTS 2.0复活逝者声音合适吗?
  • GLM-4.6V-Flash-WEB与JavaScript前端集成的方法论
  • MaterialDesignInXamlToolkit 终极指南:快速构建现代化 WPF 应用界面
  • GLM-4.6V-Flash-WEB在实时交互系统中的表现如何?
  • 2026年,别再盲目学AI了!这套90天“非编程”速成路径,专为小白设计,有人已靠它涨薪3倍(附内部学习资料)
  • image2csv终极指南:图像表格数据一键转CSV
  • 如何快速掌握TikTok数据采集:非官方API的完整实战指南
  • Dify调用描述生成接口频繁出错?3种高发场景及应对策略
  • 老年认知训练:记忆力游戏搭配AI语音互动
  • Dify触发器调试难?资深工程师教你用5个测试用例精准定位问题
  • GLM-4.6V-Flash-WEB能否替代传统OCR方案?实测告诉你答案
  • access_token频繁失效?Dify平台异常处理实战经验分享
  • GLM-4.6V-Flash-WEB在医疗影像初步筛查中的探索性应用
  • 如何正确设置Dify响应编码?,资深架构师的配置 Checklist
  • 【Dify配置优化黄金法则】:提升响应速度300%的秘密武器
  • Obsidian i18n插件深度解析:彻底告别英文插件困扰的终极解决方案
  • BiliTools AI视频总结:3分钟快速掌握B站核心内容的终极指南
  • BongoCat桌面萌宠:为数字生活注入温暖的终极伴侣
  • ComfyUI多GPU加速技术深度解析:突破显存限制的分布式计算方案
  • TikTok API深度重构指南:从零构建社交内容管理平台
  • 文档解析总卡顿?,一文掌握Dify中DOCX处理的五大优化技巧
  • 如果AI有“意识”,我们第一件事该是拔电源,还是开香槟?
  • caj2pdf:打破知网格式壁垒的专业转换工具
  • FIFA 23 Live Editor完全攻略:免费修改工具从入门到精通
  • Steamworks API拦截技术终极解析:DLC内容所有权模拟深度指南