• View和viewController的生命周期


    一、ViewController的职责

    对内管理与之关联的View,对外跟其他ViewController通信和协调。对于与之关联的View,ViewController总是在需要的时候才加载视图,并在不需要的时候卸载视图,所以也同时担当了管理应用资源的责任

    二、ViewController的生命周期

    View是指Controller的View。它作为Controler的属性,生命周期在Controller的生命周期内。就是说你的Controller不能在view释放前就释放了。

                         viewController的生命周期图

    init -> loadView ->viewDidLoad-> viewWillAppear->viewDidAppear->viewWillDisappear->viewDidDisappear->viewDidUnload->dealloc

       需要说明的是:当你alloc并init了一个ViewController时,这个ViewController应该是还没有创建view的。ViewController的view是使用了lazyInit方式创建,就是说你调用的view属性的getter:[self view]。在getter里会先判断view是否创建,如果没有创建,那么会调用loadView来创建view。loadView完成时会继续调用viewDidLoad。loadView和viewDidLoad的一个区别就是:loadView时还没有view。而viewDidLoad时view以及创建好了。

    三、view的加载过程

       

    跟随如下文字理解viewController对view加载过程:

    1 先判断子类是否重写了loadView,如果有直接调用。之后调viewDidLoad完成View的加载。

    2 如果是外部通过调用initWithNibName:bundle指定nib文件名的话,ViewController记载此nib来创建View。

    3 如果initWithNibName:bundle的name参数为nil,则ViewController会通过以下两个步骤找到与其关联的nib。

    A 如果类名包含Controller,例如ViewController的类名是MyViewController,则查找是否存在MyView.nib;

    B 找跟ViewController类名一样的文件,例如MyViewController,则查找是否存在MyViewController.nib。

    4  如果子类没有重写的loadView,则ViewController会从StroyBoards中找或者调用其默认的loadView,默认的loadView返回一个空白的UIView对象。

    注意第一步,ViewController是判断子类是否重写了loadView,而不是判断调用子类的loadView之后ViewController的View是否为空。就是说,如果子类重写了loadView的话,不管子类在loadView里面能否获取到View,ViewController都会直接调viewDidLoad完成View的加载。

    跟随以下文字理解卸载过程:

    1 系统发出警告或者ViewController本身调用导致didReceiveMemoryWarning被调用

    2 调用viewWillUnload之后释放View

    3 调用viewDidUnload

    四、模拟器的调用顺序

    我构架了这样一个环境,在该环境中有两个viewController,姑且命名为A和B,tag分别为1和2,A控制程序启动的时候即加载的界面,在A中放一个按钮,按下后会通过segue来调用到界面B;B 中页放一个按钮,通过执行

    [self dismissModalViewControllerAnimated:YES];

    来返回界面A

    然后检测所有的函数调用,依次如下

    加载A的时候依次调用

    1 initWithCoder

    1 loadView //如果说你进行了重写,会在这里调用,这一步可以参考下文

    1 viewDidLoad

    1 viewWillAppear

    1 viewWillLayoutSubviews

    1 viewDidLayoutSubviews

    1 viewDidAppear

    切换至B的时候依次调用

    2 initWithCoder             //先将2初始化

    1 prepareForSegue       //调用1的准备过度的函数,所以在该函数中可以对界面B的一些相关属性进行赋值

    2 loadView    //如果这里进行了重写

    2 viewDidLoad              //2界面加载

    1 viewWillDisappear

    2 viewWillAppear

    2 viewWillLayoutSubviews

    2 viewDidLayoutSubviews

    2 viewDidAppear

    1 viewDidDisappear

    从B切换回A的时候依次调用

    2 viewWillDisappear

    1 viewWillAppear

    1 viewDidAppear

    2 viewDidDisappear

    2 dealloc

    顺序总结下来加载依次为:加载 - 显示 - 布局

    完成顺序依次为:完成布局 - 完成显示  - 完成加载

    小注:-(void)loadView;函数如果重写,下面是一个可能的demo

    -(void)loadView

    {

       CGRect applicationFrame = [[UIScreenmainScreen] applicationFrame];

       UIView *contentView = [[UIViewalloc] initWithFrame:applicationFrame];

       contentView.backgroundColor = [UIColordarkGrayColor];

       self.view = contentView;

       

       UILabel *lab = [[UILabelalloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

       lab.text = @"HelloWorld";

       [self.viewaddSubview:lab];

    }

    loadView虽然返回值为空,但必须在函数体内对self.view进行赋值,否则会在建立该界面的时候收到如下的log信息:

    Application windows are expected to have a root view controller at the end of application launch

    具体执行顺序为:代码执行了initWithCoder之后直接调用了三次loadView函数,并且没有调用其它函数(包括viewDidLoad 、viewWillDisappear、viewWillLayoutSubviews)

    疑问:

    暂不清楚为什么会调用三次,我的猜测是:上述三个函数分别检测了一遍view是否存在,发现不存在,所以各自调用了一遍viewLoad,最后发现依然不存在,所以上述三个函数分别返回了失败,加载完成

    但矛盾的地方是:为什么上述三个函数本身没有执行到?底层到底做了什么?

    五、view和ViewController的创建阶段,关于什么时候应该干什么

    1、init

    Allocating critical data structures required by your view controller

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

    2、loadView

    Creating your view objects

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

    3、viewDidLoad

    Allocating or loading data to be displayed in your view

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

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

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

    6、viewDidUnload

    Releasing references to view objects

    Releasing data that is not needed when your view is not displayed

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

    7、dealloc

    Releasing critical data structures required by your view controller

    六、几点备注:

    1、按结构可以对iOS的所有ViewController分成两类:

    1)、主要用于展示内容的ViewController,这种ViewController主要用于为用户展示内容,并与用户交互,如UITableViewController,UIViewController。

    2)、用于控制和显示其他ViewController的ViewController。这种ViewController一般都是一个ViewController的容器。如UINavigationController,UITabbarController。它们都有一个属性:viewControllers。其中UINavigationController表示一种Stack式结构,push一个ViewController或pop一次,因此后一个ViewController一般会依赖前一个ViewController。而UITabbarController表示一个Array结构,各个ViewController是并列的。

    第一种ViewController会经常被继承,用来显示不同的数据给用户。而第二种很少被继承,除非你真的需要自定义它。

    2、当view被添加其他view中之前时,会调用viewWillAppear,而之后会调用viewDidAppear。

    当view从其他view中移出之前时,会调用viewWillDisAppear,而之后会调用viewDidDisappear。

    当view不在使用,而且是disappeared,受到内存警告时,那么viewController会将view释放并将其指向nil。

    3、由于Controller加载View时,会自动将一些View对象指向其对应的IBOutlet变量。

    所以当view被卸载时我们必须在viewDidUnload将这些变量release掉,ViewController不会自动做这件事。

    具体做法是将变量设置为空,(注意和dealloc中将变量release的区别)注意此时Controller的view属性是空的。

  • 相关阅读:
    【大话设计模式】——浅谈设计模式基础
    mongodb入门安装
    Unity3D研究之多语言用中文做KEY
    java7新特性之Try-with-resources (TWR)
    ORACLE取周、月、季、年的開始时间和结束时间
    topas命令详解
    topas解析(AIX)
    top(topas),vmstat,iostat在linux和AIX操作系统下显示情况
    怎么检查网站的死链接呢?
    网站死链接检测查询工具
  • 原文地址:https://www.cnblogs.com/PressII/p/3919144.html
Copyright © 2020-2023  润新知