2.2 视图控制器——UIViewController

UIViewController是UIKit框架中Controller部分的基础,一些复杂的Controller类也是基于UIViewController继承出来的。在开发应用程序时,所有界面也都是基于UIViewController搭建出来的。

2.2.1 UIViewController的生命周期

生命周期是指一个对象从创建出来到被释放销毁的整个过程。Objective-C和Swift都是面向对象的高级语言,为了保持内存的平衡与程序运行的高效,当需要一个对象时,它会被创建并被分配内存空间;同样,当它不再被需要时,也应该被系统释放回收。在一个UIViewController对象从创建到释放的过程中,会依次调用许多生命周期函数,了解这些函数的调用时机和功能是iOS开发者的必修课。打开Xcode开发工具,创建一个名为UIViewControllerTest的工程。工程创建出来后,系统的模板自动生成一个ViewController类,这个类继承于UIViewController并且与Main.storyboard中的初始视图控制器关联。简单来说,这个类创建了一个视图控制器,作为工程的根视图控制器,我们可以在这个类中编写相关代码来对UIViewController的生命周期进行研究。

UIViewController中与生命周期相关的函数有很多,列举如下。

Swift语言版本:

        //从xib文件进行初始化
        override init(nibName nibNameOrNil: String? , bundle nibBundleOrNil: Bundle? )
        //从归档进行初始化
        required init? (coder aDecoder: NSCoder)
        //从xib加载
        override func awakeFromNib()
        //加载视图
        override func loadView()
        //加载视图完成
        override func viewDidLoad()
        //将要布局子视图
        override func viewWillLayoutSubviews()
        //完成布局子视图
        override func viewDidLayoutSubviews()
        //收到内存警告
        override func didReceiveMemoryWarning()
        //视图将要展示
        override func viewWillAppear(_ animated: Bool)
        //视图完成展示
        override func viewDidAppear(_ animated: Bool)
        //视图将要消失
        override func viewWillDisappear(_ animated: Bool)
        //视图已经消失
        override func viewDidDisappear(_ animated: Bool)
        //析构方法
        deinit

Objective-C语言版本:

    //类的初始化方法
    + (void)initialize;
    //对象初始化方法
    - (instancetype)init;
    //从归档初始化
    - (instancetype)initWithCoder:(NSCoder *)coder;
    //从nib文件初始化
    -(void)awakeFromNib;
    //加载视图
    -(void)loadView;
    //加载视图完成
    - (void)viewDidLoad;
    //将要布局子视图
    -(void)viewWillLayoutSubviews;
    //已经布局子视图
    -(void)viewDidLayoutSubviews;
    //内存警告
    - (void)didReceiveMemoryWarning;
    //将要展示
    -(void)viewWillAppear:(BOOL)animated;
    //已经展示
    -(void)viewDidAppear:(BOOL)animated;
    //将要消失
    -(void)viewWillDisappear:(BOOL)animated;
    //已经消失
    -(void)viewDidDisappear:(BOOL)animated;
    //被释放
    (void)dealloc;

实践是最好的老师,在编程的学习中亦是如此。要了解上面方法的执行次序,跟踪程序的运行是最快的方法,先在ViewController.swift(Objective-C对应文件为ViewController.m)文件中实现这些方法,代码如下。

Swift语言版本:

        import UIKit
        var tip = 0
        class ViewController: UIViewController {
            //从xib文件进行初始化
            override init(nibName nibNameOrNil: String? , bundle nibBundleOrNil: Bundle? ) {
              super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
              tip+=1
              print("init:\(tip)")
            }
            //从归档进行初始化
            required init? (coder aDecoder: NSCoder) {
              super.init(coder: aDecoder)
              tip+=1
              print("init? :\(tip)")
            }
            //从xib加载
            override func awakeFromNib() {
              super.awakeFromNib()
              tip+=1
              print("awakeFromNib:\(tip)")
            }
            //加载视图
            override func loadView() {
              super.loadView()
              tip+=1
              print("loadView:\(tip)")
            }
            //加载视图完成
            override func viewDidLoad() {
              super.viewDidLoad()
              tip+=1
              print("viewDidLoad:\(tip)")
            }
            //将要布局子视图
            override func viewWillLayoutSubviews() {
              super.viewWillLayoutSubviews()
              tip+=1
              print("viewWillLayoutSubviews:\(tip)")
            }
            //完成布局子视图
            override func viewDidLayoutSubviews() {
              super.viewDidLayoutSubviews()
              tip+=1
              print("viewDidLayoutSubviews:\(tip)")
            }
            //收到内存警告
            override func didReceiveMemoryWarning() {
              super.didReceiveMemoryWarning()
              tip+=1
              print("didReceiveMemoryWarning:\(tip)")
            }
            //视图将要展示
            override func viewWillAppear(_ animated: Bool) {
              super.viewWillAppear(animated)
              tip+=1
              print("viewWillAppear:\(tip)")
            }
            //视图完成展示
            override func viewDidAppear(_ animated: Bool) {
              super.viewDidAppear(animated)
              tip+=1
              print("viewDidAppear:\(tip)")
            }
            //视图将要消失
              override func viewWillDisappear(_ animated: Bool) {
              super.viewWillDisappear(animated)
              tip+=1
              print("viewWillDisappear:\(tip)")
            }
            //视图已经消失
              override func viewDidDisappear(_ animated: Bool) {
              super.viewWillDisappear(animated)
              tip+=1
              print("viewDidDisappear:\(tip)")
            }
            //析构方法
              deinit {
              tip+=1
              print("deinit:\(tip)")
          }
        }

Objective-C语言版本:

      #import "ViewController.h"
      int tip=0;
      @interface ViewController ()

      @end

      @implementation ViewController
      +(void)initialize{
          [super initialize];
          NSLog(@"%d initialize", ++tip);
      }
      - (instancetype)init
      {
          self = [super init];
          if (self) {

          }
          NSLog(@"%d init", ++tip);
          return self;
      }
      - (instancetype)initWithCoder:(NSCoder *)coder
      {
          self = [super initWithCoder:coder];
          if (self) {
          }
          NSLog(@"%d initWithCoder", ++tip);
          return self;
      }
      -(void)awakeFromNib{
          [super awakeFromNib];
          NSLog(@"%d awakeFromNib", ++tip);
      }
      -(void)loadView{
          [super loadView];
          NSLog(@"%d loadView", ++tip);
      }

      - (void)viewDidLoad {
          [super viewDidLoad];
          NSLog(@"%d viewDidLoad", ++tip);
      }

      -(void)viewWillLayoutSubviews{
          [super viewWillLayoutSubviews];
          NSLog(@"%d viewWillLayoutSubviews", ++tip);
      }
      -(void)viewDidLayoutSubviews{
          [super viewDidLayoutSubviews];
          NSLog(@"%d viewDidLayoutSubviews", ++tip);
      }
      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning];
          NSLog(@"%d didReceiveMemoryWarning", ++tip);
      }
      -(void)viewWillAppear:(BOOL)animated{
          [super viewWillAppear:animated];
          NSLog(@"%d viewWillAppear", ++tip);
      }
      -(void)viewDidAppear:(BOOL)animated{
          [super viewDidAppear:animated];
          NSLog(@"%d viewDidAppear", ++tip);
      }
      -(void)viewWillDisappear:(BOOL)animated{
          [super viewWillDisappear:animated];
          NSLog(@"%d viewWillDisAppear", ++tip);
      }
      -(void)viewDidDisappear:(BOOL)animated{
          [super viewDidDisappear:animated];
          NSLog(@"%d viewDidDisappear", ++tip);
      }
      -(void)dealloc{
          NSLog(@"%d dealloc", ++tip);
      }
      @end

上面的代码创建了一个全局变量tip,使用这个变量对程序的运行过程进行标记。ViewController是UIViewController的一个子类,在子类中覆写父类的方法时,需要先调用父类的此方法。self和super这两个关键字一直是初学者的噩梦,甚至在许多时候,拥有一定开发经验的开发者也不能完全理解其意义。在这里读者只需要记住,在实例方法中,self指调用这个方法的实例,即当前类的一个实例对象,如果用super调用方法,就代表从父类中找这个方法实现;而在类方法或静态方法中self指当前类,如果用super调用方法,代码就从父类中找当前类方法或静态方法的实现。

提示 Objective-C代码中的dealloc方法是唯一一个不需要并且也不能在实现时调用父类方法的函数,这个函数在ARC(自动引用计数)环境中不再被开发者需要,但是开发者依然可以重写这个函数来监测内存的释放情况。Swift代码中的deinit方法与之类似。

运行工程,在Xcode的调试区会打印出如图2-2所示的信息。

图2-2 程序运行时的打印信息

从打印信息中可以清晰地看到UIViewController被创建的过程中依次调用的方法,但是这些信息并不全面,并没有体现出UIViewController销毁时的过程,也没有展现从不同来源初始化的UIVewController生命周期的不同。在列出的生命周期方法中,initialize方法比较特殊,这个函数只有在Objective-C语言的工程中会用到,并不会在每次创建对象时都调用,只在这个类第一次创建对象的时候调用做一些类的准备工作。实际上,如果有继承的子类,并且子类没有实现initialize方法,第一次创建子类对象时,父类就会代替子类再调用一次initialize方法。

init和initWithCoder方法作用相似,都是对对象做初始化工作,如果从代码进行初始化,就会调用init方法;如果从归档文件进行初始化,就会调用initWihCoder方法。awakeFromNib方法会在从xib或storyboard中加载的UIViewController将要被激活时被调用。

loadView方法是开始加载UI视图的初始方法,这个方法除非开发者手动调用,否则在UIViewController的生命周期中只会被调用一次。

viewDidLoad方法在视图已经加载完成后会被调用,因为这个函数被调用的时候,视图控制器的基本系统功能已经初始化完成,开发者一般会将一些Controller额外定义功能的初始化工作放在这个函数中。

· viewWillAppear方法在视图即将显示的时候调用。

· viewWillLayoutSubviews方法在视图将要布局其子视图时被调用。

· viewDidLayoutSubviews方法在视图布局完成其子视图时被调用。

· viewDidAppear方法在视图已经显示后被调用。

上面这些方法是UIViewController从创建到展现出来的过程中调用的生命周期函数,这些只是一半,UIViewController被释放和销毁的过程由如下方法完成:

· viewWillDisappear方法在视图将要消失时调用,开发者可以在其中做一些数据清理的操作。

· viewDidDisappear方法在视图已经消失时被调用。

· dealloc或deinit方法是对象的销毁方法,在对象被释放时调用,开发者可以通过在其中打印信息的方式检查一个类是否存在内存泄漏等问题。

2.2.2 UIViewController的视图层级结构

UIViewController自带一个UIView类型的view视图,这个view平铺在屏幕上,是视图控制器的根视图,在视图控制器中添加其他UI控件都是添加在这个view上。UIView类通过addSubview方法添加子view视图,子view视图也可以继续使用addSubview方法添加自己的子视图。在第1章中,Hello World标签实际上就是添加在视图控制器的根视图上的一个子Label标签视图,iOS系统通过这样的层级结构管理整个UI体系。