iOS的视图生命周期原理(loadView)

简介

总结一下视图的生命周期原理,其实在开发中我们只需要知道怎么用就足以应付日常的任务了,了解一些原理可以帮助我们更灵活的应用。

目录

1、视图周期
2、loadView的设计目的
3、loadView的作用
4、控制器加载视图的流程
5、实际应用的案例

视图的生命周期验证

1、打印View的生成方法

- (void)awakeFromNib {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

- (void)loadView {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

//视图加载完成
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

// 视图将要显示
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

// 视图将要布局子视图
- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

//视图完成布局子视图
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

/// 视图完成显示
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

// 视图将要消失
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

//视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
    
    // 判断当前视图是否已经被加载
    if (self.isViewLoaded && self.view.window == nil) {
        // 释放根视图
        self.view = nil;
    }
}

运行打印的结果:


iOS的视图生命周期原理(loadView)_第1张图片
视图出现顺序

可以看到先走了awakeFromNib,awakeFromNib是什么,顾名思义,系统从Nib加载,Nib也就是Xib,先去加载Xib的视图,然后执行loadView,然后经历了视图加载,视图即将出现,添加用户自定义的子视图,视图即将消失,已经消失这几个阶段。
视图出现之后就是我们眼睛可以看得见的东西,我们可以注意到,在viewDidLoad之前,有一个loadView,这个方法是做什么呢?

我们来验证一下:

说明:(OneViewController 关联了storyboard)

一、重新这个方法,里边使用self.view

- (void)loadView {
    self.view.backgroundColor = [UIColor redColor];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}

运行发现,程序崩溃了,这是为什么???并且self.view = nil;
看崩溃断点:


iOS的视图生命周期原理(loadView)_第2张图片
错误线程

发现控制器的父类在调用view这个方法之后,子类OneViewController调用loadView这个方法,如此循环。。。为什么父类创建一个view会触发子类的loadView的方法,子类又去触发父类的方法?

再来验证二:
在这个方法里不去调用self.view

- (void)loadView {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

运行发现,加载了storyboard 的视图出来,为什么此时不崩溃?self.view这句代码是出了什么问题?

验证三:

- (void)loadView {
    self.view = [[UIView alloc] init];
    self.view.backgroundColor = [UIColor redColor];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}

运行也不崩溃,但是视图是红色的。

验证四:

- (void)loadView {
    self.view = [[UIView alloc] init];
    self.view.backgroundColor = [UIColor redColor];
    [super loadView];
    NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}

运行结果是加载了storyboard 的视图出来。

根据上边几个验证,我们发现,验证一中,会死循环,是因为在loadView方法里使用到控制器的根视图view的时候,当前view是空的,当系统发现view根本不存在,内存没有它的地址的时候就会尝试去调用loadView的方法,自然就触发了子控制器的loadView, 然后在子控制器又使用了self.view,所以会形成一个死循环。通过验证三我们知道,初始化一个UIView并且赋值给self.view就会不会崩溃,到这样我们已经可以看到loadView的作用就是初始化一个view。

结论:loadView的设计目的就是创建视图控制器的根视图,是开发者创建一个控制器所有UI的基础

归纳以下几点:
1、加载一个控制器之前,系统会先在loadView方法中判断是否已经有一个View可以作为根视图
2、如果根视图被使用的时候,并且是nil的,就会尝试去创建一个view并赋值给控制器的根视图
3、原则上,你不需要去调用loadView方法,因为系统自动调用了
4、如果在代码中,你重写了loadView,一旦调用 super,会执行系统默认的加载 stroyboard 或者 xib 的工作,loadView内super之前的代码全部白写,也就是,只要你不调用super,系统就会从你自定义的View中加载出来,这时候与 stroyboard 或者 xib 无关。

问:我们已经知道,loadView的出现就是为了创建一个根视图,并且系统自动帮助我们调用了,那么,它的出现对于开发者有什么意义呢?
答:它出现的意义就是可以让开发者可以自定义根视图。例如:使用loadView方法触发nib中UIView的加载。

我们用一个流程图来总结:

iOS的视图生命周期原理(loadView)_第3张图片
加载视图流程

创建一个根视图的顺序:
1、是否有一个view可用
2、如果有,直接显示这个view;如果没有,则系统自动调用loadView方法去生成一个view
3、当重写了loadView方法,并在里边自定义了view则直接使用这个view作为根视图
4、如果调用了[super loadView]; ,系统就优先去加载stroyboard或者xib的视图

loadView的实际应用

我们先看看一个实际开发中的问题,加载一个Xib控制器。

/// 视图加载完成
- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *clickBtn = [[UIButton alloc]initWithFrame: CGRectMake(100, 200,[UIScreen mainScreen].bounds.size.width - 200, 50)];
    [clickBtn setTitle:@"跳转到Xib控制器" forState:UIControlStateNormal];
    [self.view addSubview:clickBtn];
    
    [clickBtn addTarget:self action:@selector(clickBtnClick) forControlEvents:UIControlEventTouchUpInside];
    
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

- (void)clickBtnClick{
    XibViewController *vc = [[XibViewController alloc]initWithNibName:@"XibViewController" bundle:nil];
//    [vc loadView];
    vc.beautifulImageView.image = [UIImage imageNamed:@"img2"];
    vc.titleLab.text = @"我想看美女";
    [self presentViewController:vc animated:YES completion:nil];
}
iOS的视图生命周期原理(loadView)_第4张图片
Xib视图控制器

运行后点击按钮跳转到Xib控制器,发现即使我在跳转之前重新设置了图片和文字,但是加载的还是默认的,那怎么办?
上边已经论证了,loadView有一个方法就是用来加载控制器的根视图的,修改之后出现默认的图片和文字是因为控制器push出来之后,根视图也会加载出来,但是系统默认会去Xib文件中寻找是否有可用的view,发现有一个默认的view,直接加载出来,即使你修改也没用。

解决办法:重写loadview的方法,让系统去使用我们自定义的视图view

- (void)clickBtnClick{
    XibViewController *vc = [[XibViewController alloc]initWithNibName:@"XibViewController" bundle:nil];
    [vc loadView];
    vc.beautifulImageView.image = [UIImage imageNamed:@"img2"];
    vc.titleLab.text = @"我想看美女";
    [self presentViewController:vc animated:YES completion:nil];
}

运行
iOS的视图生命周期原理(loadView)_第5张图片
loadview使用后

大功告成!

你可能感兴趣的:(iOS的视图生命周期原理(loadView))