专注、坚持

探究 UIViewController 生命周期

2017.03.10 by kingcos

前言

对象的生命周期一直是开发者所需要关心的,教授 CS193p 的老师 Paul 也详细的讲述了 UIViewController 的生命周期。为了记述这一过程,故作此文。由于 Xcode 提供了纯代码和 Storyboard(Xib 同理)两种布局 UI 的方式,因此初始化部分略有不同。

为了方便观察,我创建了一个 BaseViewController,继承自原本的 UIViewController,重写其中的生命周期方法,并让后续新的控制器继承自该控制器,以便观察。

本文对应的 Demo 可以在 https://github.com/kingcos/UIViewController-UIView-LifecycleDemo 查看、下载。

Structure

初始化阶段

Storyboard

init(coder:)
awakeFromNib()

init(coder:)

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

awakeFromNib()

  • 当使用 Storyboard 时,该方法会被调用。
  • 当调用该方法时,将保证所有的 outletaction 连接已经完成。
  • 该方法内部必须调用父类该方法,虽然其默认实现为空,但 UIKit 中许多类的该方法为非空。
  • 由于控制器中对象的初始化顺序不能确定,所以构造器中不应该向其他对象发送消息,而应当在 awakeFromNib() 中安全地发送。
  • 通常使用 awakeFromNib() 可以进行在设计时无法完成的必要额外设置。

纯代码

init(nibName:bundle:) - NibName: nil, Bundle: nil

init(nibName:bundle:)

  • 当使用纯代码创建控制器,控制器的构造器为 init(nibName:bundle:)
  • 虽然使用代码创建时调用了该构造器,但传入的参数均为 nil。

视图从加载到可视阶段

  • 该阶段 Storyboard 与纯代码同理。
loadView()
viewDidLoad()
viewWillAppear(_:)
viewWillLayoutSubviews() - Optional((87.5, 391.0, 200.0, 30.0))
viewDidLayoutSubviews() - Optional((87.5, 391.0, 200.0, 30.0))
viewDidAppear(_:)

loadView()

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

viewDidLoad()

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

viewWillAppear(_:)

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

viewWillLayoutSubviews()

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

viewDidLayoutSubviews()

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

viewDidAppear(_:)

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

视图从可视到销毁阶段

viewWillDisappear(_:)
viewDidDisappear(_:)
deinit

viewWillDisappear(_:)

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

viewDidDisappear(_:)

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

deinit

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

Optional - didReceiveMemoryWarning()

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

Extension

Rotation

willTransition(to:with:)
viewWillLayoutSubviews() - Optional((67.5, 269.5, 240.0, 128.0))
viewDidLayoutSubviews() - Optional((213.5, 123.5, 240.0, 128.0))
viewWillLayoutSubviews() - Optional((213.5, 123.5, 240.0, 128.0))
viewDidLayoutSubviews() - Optional((213.5, 123.5, 240.0, 128.0))
viewWillLayoutSubviews() - Optional((213.5, 123.5, 240.0, 128.0))
viewDidLayoutSubviews() - Optional((213.5, 123.5, 240.0, 128.0))
  • view 转变,会调用 willTransition(to:with:) 方法。
  • 当屏幕旋转,viewbounds 改变,其内部的子控件也需要按照约束调整为新的位置,因此也调用了 viewWillLayoutSubviews()viewDidLayoutSubviews()

Present & Dismiss

viewWillDisappear()
viewDidDisappear()
viewDidDisappear()
viewWillAppear()
viewDidAppear()
  • 当在一个控制器内 Present 新的控制器,原先的控制器并不会销毁,但会消失,因此调用了 viewWillDisappearviewDidDisappear 方法。
  • 如果新的控制器 Dismiss,即清除自己,原先的控制器会再一次出现,因此调用了其中的 viewWillAppearviewDidAppear 方法。

死循环

class LoopViewController: UIViewController {
	override func loadView() {
		print(#function)
	}

	override func viewDidLoad() {
		print(#function)
		let _ = view
	}
}

loadView() 中没有加载 view,viewDidLoad() 会一直调用 loadView() 加载 view,因此构成了死循环,程序即卡死。

loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()

Reference

  • CS193P_2017
  • UIViewController 相关生命周期总结

哆哆女性网seo教程培训班医疗网站seo怎么做月嫂公司怎么起名字2020最新印度动作电影情侣头像制作网站张字起取名大全成语起名子男孩名字算命大师算命软件虞城陈富磊猩球崛起猩猩名字网站seo入门河南商丘有什么美食终极武力异地汇款手续费论语周易取名字个体经营起名bt种子是什么意思月亮起名设计素材网站有那些pururin邱姓起名男孩姓名大全aecs6门户网站建设先进性网上起名字测试宝宝起名最后一个字该起什么琴行起那个名字好天门网站优化郑州网站建设哪家公司好名字吧起名免费网如何给文化公司起名淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻不负春光新的一天从800个哈欠开始有个姐真把千机伞做出来了国产伟哥去年销售近13亿充个话费竟沦为间接洗钱工具重庆警方辟谣“男子杀人焚尸”男子给前妻转账 现任妻子起诉要回春分繁花正当时呼北高速交通事故已致14人死亡杨洋拄拐现身医院月嫂回应掌掴婴儿是在赶虫子男孩疑遭霸凌 家长讨说法被踢出群因自嘲式简历走红的教授更新简介网友建议重庆地铁不准乘客携带菜筐清明节放假3天调休1天郑州一火锅店爆改成麻辣烫店19岁小伙救下5人后溺亡 多方发声两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#青海通报栏杆断裂小学生跌落住进ICU代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了当地回应沈阳致3死车祸车主疑毒驾武汉大学樱花即将进入盛花期张立群任西安交通大学校长为江西彩礼“减负”的“试婚人”网友洛杉矶偶遇贾玲倪萍分享减重40斤方法男孩8年未见母亲被告知被遗忘小米汽车超级工厂正式揭幕周杰伦一审败诉网易特朗普谈“凯特王妃P图照”考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼恒大被罚41.75亿到底怎么缴男子持台球杆殴打2名女店员被抓校方回应护栏损坏小学生课间坠楼外国人感慨凌晨的中国很安全火箭最近9战8胜1负王树国3次鞠躬告别西交大师生房客欠租失踪 房东直发愁萧美琴窜访捷克 外交部回应山西省委原副书记商黎光被逮捕阿根廷将发行1万与2万面值的纸币英国王室又一合照被质疑P图男子被猫抓伤后确诊“猫抓病”

哆哆女性网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化