iOS UIViewController的周期以及dealloc方法的使用

ViewController的生命周期是指在应用程序运行过程中,ViewController实例从创建到销毁的整个过程。在这个过程中,ViewController会经历一系列的生命周期方法,这些方法可以帮助开发者管理ViewController及其相关的视图和逻辑。

ViewController的生命周期可以分为以下几个阶段:

创建阶段:在应用程序启动时,系统会根据需要创建ViewController实例。在创建阶段,ViewController会调用一些生命周期方法。当你alloc并init了一个ViewController时,这个ViewController应该是还没有创建view的。ViewController的view是使用了lazyInit方式创建,就是说你调用的view属性的getter:[self view]。

视图加载阶段:在应用程序需要显示ViewController的视图时,系统会调用ViewController的loadView方法。在这个方法中,ViewController会创建并加载视图控制器的视图。在view属性的getter里会先判断view是否创建,如果没有创建,那么会调用loadView来创建view。loadView完成时会继续调用viewDidLoad。loadView和viewDidLoad的一个区别就是:loadView时还没有view。而viewDidLoad时view以及创建好了。

视图控制器显示阶段:在视图加载完成后,ViewController会进入视图控制器显示阶段。在这个阶段,ViewController会调用一些生命周期方法,例如viewDidLoad、viewWillAppear:和viewDidAppear:。当view被添加其他view中之前时,会调用viewWillAppear,而之后会调用viewDidAppear。当view从其他view中移出之前时,会调用viewWillDisAppear,而之后会调用viewDidDisappear。

视图控制器销毁阶段:在应用程序不再需要ViewController时,系统会销毁ViewController实例。在销毁阶段,ViewController会调用一些生命周期方法,例如viewWillDisappear:和viewDidDisappear:。

ViewController的生命周期是一个复杂的过程,开发者需要了解这个过程,才能正确地管理ViewController及其相关的视图和逻辑。

注意:

1、init里不要出现创建view的代码。良好的设计,在init里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。init里不要调用self.view,否则会导致viewcontroller创建view。(因为view是lazyinit的)(文章下方细说)。

2、loadView中只初始化view,一般用于创建比较关键的view如tableViewController的tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非关键的view。如果你是从nib文件中创建的viewController在这里一定要首先调用super的loadView方法,但建议不要重载这个方法。

3、viewDidLoad 这时候view已经有了,最适合创建一些附加的view和控件了。有一点需要注意的是,viewDidLoad会调用多次(viewcontroller可能多次载入view,参见图2)。

4、viewWillAppear 这个一般在view被添加到superview之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和navigationbar颜色)。

5、viewDidAppear 一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。

6、viewDidUnload 这时候viewController的view已经是nil了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的view释放了。比如你在viewcontroller的view上加了一个label,而且这个label是viewcontroller的属性,那么你要把这个属性设置成nil,以免占用不必要的内存,而这个label在viewDidLoad时会重新创建。

loadView和viewDidLoad的区别:

loadView时view还没有生成,viewDidLoad时,view已经生成了,loadView只会被调用一次,而viewDidLoad可能会被调用多次(View可能会被多次加载),当view被添加到其他view中之前,会调用viewWillAppear,之后会调用viewDidAppear。当view从其他view中移除之前,调用viewWillDisAppear,移除之后会调用viewDidDisappear。当view不再使用时,受到内存警告时,ViewController会将view释放并将其指向为nil。

init

初始化ViewController本身。

loadView

当View需要被展示而它却是nil时,ViewController会调用该方法。

如果代码维护View的话需要重写此方法,使用xib维护View的话不用重写。

viewDidLoad

执行完loadView后执行viewDidLoad,loadView还没有View,而viewDidLoad时View已经创建好了,一般的控件在此方法初始化。

viewWillAppear

UIViewController对象的视图即将加入窗口时调用。

viewDidAppear

UIViewController对象的视图已经加入到窗口时调用。

viewWillDisappear

UIViewController对象的视图即将消失、被覆盖或是隐藏时调用。

viewDidDisappear

UIViewController对象的视图已经消失、被覆盖或是隐藏时调用。

viewDidUnload

一般发生在内存警告时。这时候viewController的view已经是nil了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的view释放了。比如你在viewcontroller的view上加了一个label,而且这个label是viewcontroller的属性,那么你要把这个属性设置成nil,以免占用不必要的内存,而这个label在viewDidLoad时会重新创建。

dealloc

释放其他资源或内存。

注意

不要在loadView中调用父类方法[super loadView],因为这会影响CPU性能。

切换前后台不会调用viewWillAppear。

代码实现:

首先我们在ViewController中创建一个按钮,然后按钮的事件函数是跳转到另一个视图控制器。

#import "ViewController.h"

#import "QieHuanViewController.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {

UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];

button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside\]; \[button setTitle:@"切换视图" forState:UIControlStateNormal\]; button.frame = CGRectMake(100, 700, 100, 80); \[self.view addSubview:button\]; } - (void) press { QieHuanViewController \*qieHuanView = \[\[QieHuanViewController alloc\] init\]; \[self presentViewController:qieHuanView animated:YES completion:nil\]; } @end 然后在被跳转的视图控制器中使用ViewController生命周期相关的方法: #import "QieHuanViewController.h" #import "ViewController.h" @interface QieHuanViewController () @end @implementation QieHuanViewController //加载View的时候调用 - (void)loadView { self.view = \[\[UIView alloc\] init\]; NSLog(@"view正在加载"); } //view加载完成后调用 - (void)viewDidLoad { self.view.backgroundColor = \[UIColor orangeColor\]; UIView \*subView = \[\[UIView alloc\] initWithFrame:CGRectMake(100, 300, 200, 200)\]; subView.backgroundColor = \[UIColor blueColor\]; \[self.view addSubview:subView\]; UIButton \*button = \[UIButton buttonWithType:UIButtonTypeSystem\]; \[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside\]; \[button setTitle:@"切换视图" forState:UIControlStateNormal\]; button.frame = CGRectMake(100, 700, 100, 80); \[self.view addSubview:button\]; } //view将要显示出来时调用 - (void)viewWillAppear:(BOOL)animated { NSLog(@"view即将显示"); } //view已经显示出来时调用 - (void)viewDidAppear:(BOOL)animated { NSLog(@"view已经显示"); } //view将要消失的时候调用 - (void)viewWillDisappear:(BOOL)animated { NSLog(@"view将要消失"); } //view已经消失的时候调用 - (void)viewDidDisappear:(BOOL)animated { NSLog(@"view已经消失"); } - (void)press { \[self dismissViewControllerAnimated:YES completion:nil\]; } @end 演示结果: ![](https://i-blog.csdnimg.cn/direct/d2c1b868f35d4c8dab6a8d23855f44df.png)![](https://i-blog.csdnimg.cn/direct/cc8ad3bc50c54451a14f2edeb8634d51.png) ![](https://i-blog.csdnimg.cn/direct/e777f45d71ae4f9abeadc66b46acfdc9.png)![](https://i-blog.csdnimg.cn/direct/b38d63463ab74a6885cd6ae2d25ba8b4.png) loadView 死循环,loadView 及使用loadView中初始化View注意的问题 在我写上方的代码时,一开始只是在重写loadView时写了NSLog的代码,于是发生了死循环,我查阅了一些资料找到了问题所在: 首先,我的loadView方法发生死循环的原因: ViewController中的loadView方法中没有做任何实例化self.view的操作。如没有执行\[supper loadView\]或者\[\[UIView alloc\] init\]; 在viewDidLoad中调用了self.view。 没有XIB。 满足了以上三个条件,代码必定发生死循环。当self.view没有实例化的时候,在viewDidLoad中调用了self.view,因为self.view为nil,所以又回调到loadView来,但是loadView没有对其实例化,因此跑完loadView又跑到了viewDidLoad,这样就产生了死循环。 那么如何解决死循环呢?处理方式有三: 在loadView中,使用已实例化的View对Self.View进行赋值。注:是使用=号赋值,而不是使用\[self.view addSubView\]因为此时self.view 是空指针,执行ADD操作会崩溃的。 在loadView中添加一句\[Supper LoadView\],不过不建议这样写 把整个-(void)loadView 屏蔽掉。让父类自己来创建一个view。这个是最常见的,因为ViewController产生的时候默认代码中是把这段代码给注释了的。当然这里我们要说ViewController的生命周期不能屏蔽它,显然这个方法是不能用的。 ## **在iOS的`dealloc`方法中,‌主要进行的是释放对象所拥有的资源以及解除相关的监听和通知。‌** * **释放资源** :‌当对象的引用计数器为0时,‌表示该对象不再被任何地方引用,‌此时系统会自动向对象发送`dealloc`消息。‌在`dealloc`方法中,‌应该释放对象所拥有的所有资源,‌包括Objective-C对象和非Objective-C对象。‌这是因为ARC(‌自动引用计数)‌机制会自动生成`.cxx_destruct`方法来在`dealloc`中自动添加释放代码,‌但有些资源需要手动管理,‌如文件描述符、‌套接字等稀缺资源,‌这些资源不应该等到`dealloc`方法被调用时才释放,‌因为这样做可能会让这些资源长时间被占用。‌通常的做法是实现一个额外的方法来管理这些资源的生命周期,‌确保在不再需要时能够及时释放。‌ * **解除监听和通知** :‌如果对象使用了`NSNotificationCenter`或其他类似的机制进行监听或注册通知,‌那么在`dealloc`方法中应该注销这些监听,‌以防止通知系统继续向已回收的对象发送通知。‌ * **调用父类的`dealloc`方法** :‌在重写`dealloc`方法时,‌必须调用父类的`dealloc`方法(‌`[super dealloc]`)‌,‌并且这行代码应放在自定义`dealloc`方法的最后。‌这是因为父类可能包含了一些清理工作,‌如释放继承自父类的资源或执行一些必要的清理操作。‌ 通过上述操作,‌可以确保对象的资源被正确、‌及时地回收,‌避免内存泄漏和其他潜在问题

相关推荐
buyue__1 小时前
MacOS解决局域网“没有到达主机的路由 no route to host“
macos
吴Wu涛涛涛涛涛Tao6 小时前
深入理解 Swift Codable:从基础到进阶
ios
明月看潮生11 小时前
青少年编程与数学 01-011 系统软件简介 05 macOS操作系统
macos·青少年编程·操作系统·系统软件·编程与数学
Jouzzy12 小时前
【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越狱教程
安全·ios·iphone
二流小码农1 天前
鸿蒙开发:实现一个标题栏吸顶
android·ios·harmonyos
season_zhu1 天前
iOS开发:关于日志框架
ios·架构·swift
Hello.Reader1 天前
Git 安装全攻略Linux、macOS、Windows 与源码编译
linux·git·macos
Hope Fancy1 天前
macOS 连接 Docker 运行 postgres,使用navicat添加并关联数据库
macos·docker·postgresql
John Song1 天前
macOS 上使用 Homebrew 安装redis-cli
数据库·redis·macos