通过代码分析Swift的UIViewController生命周期

环境:macOS10.13.6,xcode10.1,swift4.2
在xcode新建swift测试工程,两个UIViewController(ViewController.swift和FirstViewController.swift)

ViewController.swift代码如下:


import UIKit
import Foundation

class ViewController: UIViewController {

    override func loadView() {
        super.loadView();
        print("主页面:loadView");
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        print("主页面:viewDidLoad");
        
        let main_width:CGFloat = self.view.frame.size.width;
        var padding_top:CGFloat = 30.0;
        let padding_left:CGFloat = 10.0;
        let button_height:CGFloat = 40.0;
        let button_width:CGFloat = (main_width-padding_left*3)/2;
        let button_x:CGFloat = padding_left;
        
        padding_top += button_height + 10.0;
        let loginButton = UIButton.init(type: UIButton.ButtonType.custom);
        loginButton.setTitle("打开子页面", for: UIControl.State.normal);
        loginButton.setTitleColor(UIColor.blue, for: UIControl.State.normal);
        loginButton.layer.cornerRadius = 0.5;
        loginButton.backgroundColor = UIColor.lightGray;
        loginButton.clipsToBounds = true;
        loginButton.frame = CGRect(x:button_x,y:padding_top,width:button_width,height:button_height);
        loginButton.addTarget(self, action: #selector(showBtnClick), for: UIControl.Event.touchUpInside);
        self.view.addSubview(loginButton);
    }

    @objc func showBtnClick(){
        print("主页面:Show Button Click");
        present(FirstViewController(), animated: true, completion: nil);
    }

    override func viewWillAppear(_ animated: Bool) {
        print("主页面:viewWillAppear");
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews();
        print("主页面:viewWillLayoutSubviews");
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews();
        print("主页面:viewDidLayoutSubviews");
    }
    
    override func viewDidAppear(_ animated: Bool) {
        print("主页面:viewDidAppear");
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        print("主页面:viewWillDisappear");
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        print("主页面:viewDidDisappear");
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning();
        print("主页面:didReceiveMemoryWarning");
    }
    
    deinit {
        print("主页面:deinit");
    }
}

FirstViewController.swift代码如下


import UIKit

class FirstViewController: UIViewController {

    init() {
        super.init(nibName: nil, bundle: nil)
        print("子页面:init()");
    }
    
    required init?(coder aDecoder: NSCoder) {
        print("子页面:required init?(coder aDecoder: NSCoder)");
        fatalError("init(coder:) has not been implemented")
    }
    
    override func loadView() {
        super.loadView();
        print("子页面:loadView");
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        print("子页面:viewDidLoad");
        
        self.view.backgroundColor = UIColor.yellow;
        
        let main_width:CGFloat = self.view.frame.size.width;
        //let main_height:CGFloat = self.view.frame.size.height;
        let padding_top:CGFloat = 30.0;
        let padding_left:CGFloat = 10.0;
        let button_height:CGFloat = 40.0;
        let button_width:CGFloat = (main_width-padding_left*3)/2;
        let button_x:CGFloat = padding_left;
        
        let backButton = UIButton.init(type: UIButton.ButtonType.custom);
        backButton.setTitle("返回主页面", for: UIControl.State.normal);
        backButton.setTitleColor(UIColor.blue, for: UIControl.State.normal);
        backButton.layer.cornerRadius = 0.5;
        backButton.backgroundColor = UIColor.lightGray;
        backButton.clipsToBounds = true;
        backButton.frame = CGRect(x:button_x,y:padding_top,width:button_width,height:button_height);
        backButton.addTarget(self, action: #selector(backBtnClick), for: UIControl.Event.touchUpInside);
        self.view.addSubview(backButton);
    }
    
    @objc func backBtnClick(){
        print("子页面:Back Button Click");
        dismiss(animated: true, completion: nil)
    }

    override func viewWillAppear(_ animated: Bool) {
        print("子页面:viewWillAppear");
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews();
        print("子页面:viewWillLayoutSubviews");
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews();
        print("子页面:viewDidLayoutSubviews");
    }
    
    override func viewDidAppear(_ animated: Bool) {
        print("子页面:viewDidAppear");
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        print("子页面:viewWillDisappear");
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        print("子页面:viewDidDisappear");
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning();
        print("子页面:didReceiveMemoryWarning");
    }
    
    deinit {
        print("子页面:deinit");
    }
}

用模拟器运行,点击主页面的【打开子页面】按钮后显示子页面,点击子页面的【返回主页面】按钮后返回主页面,控制台打印出来的日志如下:

 1 主页面:loadView
 2 主页面:viewDidLoad
 3 主页面:viewWillAppear
 4 主页面:viewWillLayoutSubviews
 5 主页面:viewDidLayoutSubviews
 6 主页面:viewDidAppear
 7 主页面:Show Button Click
 8 子页面:init()
 9 子页面:loadView
10 子页面:viewDidLoad
11 主页面:viewWillDisappear
12 子页面:viewWillAppear
13 子页面:viewWillLayoutSubviews
14 子页面:viewDidLayoutSubviews
15 主页面:viewWillLayoutSubviews
16 主页面:viewDidLayoutSubviews
17 子页面:viewDidAppear
18 主页面:viewDidDisappear
19 子页面:Back Button Click
20 子页面:viewWillDisappear
21 主页面:viewWillAppear
22 主页面:viewWillLayoutSubviews
23 主页面:viewDidLayoutSubviews
24 主页面:viewDidAppear
25 子页面:viewDidDisappear
26 子页面:deinit
27 主页面:viewWillLayoutSubviews
28 主页面:viewDidLayoutSubviews

注意第10~12行的log,【子页面:viewDidLoad】打印之后,打印的是【主页面:viewWillDisappear】,然后打印【子页面:viewWillAppear】。说明:子页面view加载到内存之后,主页面view将从视图层移出,接着子页面view将被添加到视图层。

注意第17行和18行的log,【子页面:viewDidAppear】打印之后,打印【主页面:viewDidDisappear】。说明:子页面view被添加到视图层后,主页面view从视图层移除。

分析第24行~26行的log,【主页面:viewDidAppear】打印之后,打印【子页面:viewDidDisappear】,然后打印【子页面:deinit】。说明:从子页面返回到主页面后,先主页面view被添加到视图层,然后子页面view从视图层移除,最后子页面控制器被销毁。

分析结果Swift的UIViewController生命周期为
init --> loadView --> viewDidLoad --> viewWillAppear --> viewWillLayoutSubviews --> viewDidLayoutSubviews --> viewDidAppear --> viewWillDisappear --> viewDidDisappear --> deinit

附录各函数说明:
init(coder:)
当使用 Storyboard 时,控制器的构造器为 init(coder:)。
该构造器为必需构造器,如果重写其他构造器,则必须重写该构造器。
该构造器为可失败构造器,即有可能构造失败,返回 nil。
该方法来源自 NSCoding 协议,而 UIViewController 遵从这一协议。
该方法被调用意味着控制器有可能(并非一定)在未来会显示。
在控制器生命周期中,该方法只会被调用一次。

loadView()
loadView() 即加载控制器管理的 view。
不能直接手动调用该方法;当 view 被请求却为 nil 时,该方法加载并创建 view。
若控制器有关联的 Nib 文件,该方法会从 Nib 文件中加载 view;如果没有,则创建空白 UIView 对象。
如果使用 Interface Builder 创建 view,则务必不要重写该方法。
可以使用该方法手动创建视图,且需要将根视图分配为 view;自定义实现不应该再调用父类的该方法。
执行其他初始化操作,建议放在 viewDidLoad() 中。

viewDidLoad()
view 被加载到内存后调用 viewDidLoad()。
重写该方法需要首先调用父类该方法。
该方法中可以额外初始化控件,例如添加子控件,添加约束。
该方法被调用意味着控制器有可能(并非一定)在未来会显示。
在控制器生命周期中,该方法只会被调用一次。

viewWillAppear(_:)
该方法在控制器 view 即将添加到视图层次时以及展示 view 时所有动画配置前被调用。
重写该方法需要首先调用父类该方法。
该方法中可以进行操作即将显示的 view,例如改变状态栏的取向,类型。
该方法被调用意味着控制器将一定会显示。
在控制器生命周期中,该方法可能会被多次调用。

   注意:
    如果控制器 A 被展示在另一个控制器 B 的 popover 中,
    那么控制器 B 不会调用该方法,直到控制器 A 清除。

viewWillLayoutSubviews()
该方法在通知控制器将要布局 view 的子控件时调用。
每当视图的 bounds 改变,view 将调整其子控件位置。
该方法可重写以在 view 布局子控件前做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 未起作用。
在控制器生命周期中,该方法可能会被多次调用。

viewDidLayoutSubviews()
该方法在通知控制器已经布局 view 的子控件时调用。
该方法可重写以在 view 布局子控件后做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 已经完成。
在控制器生命周期中,该方法可能会被多次调用。

viewDidAppear(_:)
该方法在控制器 view 已经添加到视图层次时被调用。
重写该方法需要首先调用父类该方法。
该方法可重写以进行有关正在展示的视图操作。
在控制器生命周期中,该方法可能会被多次调用。

viewWillDisappear(_:)
该方法在控制器 view 将要从视图层次移除时被调用。
类似 viewWillAppear(_:)。
该方法可重写以提交变更,取消视图第一响应者状态。

viewDidDisappear(_:)
该方法在控制器 view 已经从视图层次移除时被调用。
类似 viewDidAppear(_:)。
该方法可重写以清除或隐藏控件。

didReceiveMemoryWarning()
当内存预警时,该方法被调用。
不能直接手动调用该方法。
该方法可重写以释放资源、内存。

deinit
控制器销毁时(离开堆),调用该方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值