美文网首页iOS学习笔记iOS程序猿收藏swift
iOS的视图生命周期原理(loadView)

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

作者: 真的真心瓜子 | 来源:发表于2019-12-13 00:36 被阅读0次

    简介

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

    目录

    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;
        }
    }
    
    

    运行打印的结果:


    视图出现顺序

    可以看到先走了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;
    看崩溃断点:


    错误线程

    发现控制器的父类在调用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的加载。

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

    加载视图流程

    创建一个根视图的顺序:
    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];
    }
    
    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];
    }
    
    运行 loadview使用后

    大功告成!

    相关文章

      网友评论

        本文标题:iOS的视图生命周期原理(loadView)

        本文链接:https://www.haomeiwen.com/subject/algzgctx.html