由于種種原因铭污,簡(jiǎn)書等第三方平臺(tái)博客不再保證能夠同步更新,歡迎移步 GitHub:https://github.com/kingcos/Perspective/。謝謝!
Lifecycle of UIViewController in iOS
Date | Notes | Swift | Xcode |
---|---|---|---|
2017-03-10 | 首次提交 | 3.0 | 8.2.1 |
前言
對(duì)象的生命周期一直是開發(fā)者所需要關(guān)心的告丢,教授 CS193p 的老師 Paul 也詳細(xì)的講述了 UIViewController 的生命周期。為了記述這一過程损谦,故作此文岖免。由于 Xcode 提供了純代碼和 Storyboard(Xib 同理)兩種布局 UI 的方式,因此初始化部分略有不同照捡。
為了方便觀察颅湘,我創(chuàng)建了一個(gè) BaseViewController,繼承自原本的 UIViewController栗精,重寫其中的生命周期方法闯参,并讓后續(xù)新的控制器繼承自該控制器,以便觀察悲立。
本文對(duì)應(yīng)的 Demo 可以在 https://github.com/kingcos/UIViewController-UIView-LifecycleDemo 查看鹿寨、下載。
Initialization
Storyboard
OUTPUT:
init(coder:)
awakeFromNib()
init(coder:)
- 當(dāng)使用 Storyboard 時(shí)薪夕,控制器的構(gòu)造器為
init(coder:)
释移。 - 該構(gòu)造器為必需構(gòu)造器,如果重寫其他構(gòu)造器寥殖,則必須重寫該構(gòu)造器。
- 該構(gòu)造器為可失敗構(gòu)造器涩蜘,即有可能構(gòu)造失敗嚼贡,返回 nil。
- 該方法來源自 NSCoding 協(xié)議同诫,而 UIViewController 遵從這一協(xié)議粤策。
- 該方法被調(diào)用意味著控制器有可能(并非一定)在未來會(huì)顯示。
- 在控制器生命周期中误窖,該方法只會(huì)被調(diào)用一次叮盘。
awakeFromNib()
- 當(dāng)使用 Storyboard 時(shí),該方法會(huì)被調(diào)用霹俺。
- 當(dāng)調(diào)用該方法時(shí)柔吼,將保證所有的 outlet 和 action 連接已經(jīng)完成。
- 該方法內(nèi)部必須調(diào)用父類該方法丙唧,雖然默認(rèn)實(shí)現(xiàn)為空愈魏,但 UIKit 中許多類的該方法為非空。
- 由于控制器中對(duì)象的初始化順序不能確定,所以構(gòu)造器中不應(yīng)該向其他對(duì)象發(fā)送消息培漏,而應(yīng)當(dāng)在
awakeFromNib()
中安全地發(fā)送溪厘。 - 通常使用
awakeFromNib()
可以進(jìn)行在設(shè)計(jì)時(shí)無法完成的必要額外設(shè)置。
Code
OUTPUT:
init(nibName:bundle:) - NibName: nil, Bundle: nil
init(nibName:bundle:)
- 當(dāng)使用純代碼創(chuàng)建控制器牌柄,控制器的構(gòu)造器為
init(nibName:bundle:)
畸悬。 - 雖然使用代碼創(chuàng)建時(shí)調(diào)用了該構(gòu)造器,但傳入的參數(shù)均為 nil珊佣。
OUTPUT:
loadView()
viewDidLoad()
viewWillAppear
viewWillLayoutSubviews() - Optional((162.0, 308.0, 50.0, 50.0))
viewDidLayoutSubviews() - Optional((67.0, 269.0, 241.0, 129.0))
viewDidAppear
viewWillDisappear
viewDidDisappear
deinit
loadView()
-
loadView()
即加載控制器管理的 view蹋宦。 - 不能直接手動(dòng)調(diào)用該方法;當(dāng) view 被請(qǐng)求卻為 nil 時(shí)彩扔,該方法加載并創(chuàng)建 view妆档。
- 若控制器有關(guān)聯(lián)的 Nib 文件,該方法會(huì)從 Nib 文件中加載 view虫碉;如果沒有贾惦,則創(chuàng)建空白 UIView 對(duì)象。
- 如果使用 Interface Builder 創(chuàng)建 view敦捧,則務(wù)必不要重寫該方法须板。
- 可以使用該方法手動(dòng)創(chuàng)建視圖,且需要將根視圖分配為 view兢卵;自定義實(shí)現(xiàn)不應(yīng)該再調(diào)用父類的該方法习瑰。
- 執(zhí)行其他初始化操作,建議放在
viewDidLoad()
中秽荤。
viewDidLoad()
- view 被加載到內(nèi)存后調(diào)用
viewDidLoad()
甜奄。 - 重寫該方法需要首先調(diào)用父類該方法。
- 該方法中可以額外初始化控件窃款,例如添加子控件课兄,添加約束。
- 該方法被調(diào)用意味著控制器有可能(并非一定)在未來會(huì)顯示晨继。
- 在控制器生命周期中烟阐,該方法只會(huì)被調(diào)用一次。
viewWillAppear(_:)
- 該方法在控制器 view 即將添加到視圖層次時(shí)以及展示 view 時(shí)所有動(dòng)畫配置前被調(diào)用紊扬。
- 重寫該方法需要首先調(diào)用父類該方法蜒茄。
- 該方法中可以進(jìn)行操作即將顯示的 view,例如改變狀態(tài)欄的取向餐屎,類型檀葛。
- 該方法被調(diào)用意味著控制器將一定會(huì)顯示。
- 在控制器生命周期中腹缩,該方法可能會(huì)被多次調(diào)用驻谆。
注意:
如果控制器 A 被展示在另一個(gè)控制器 B 的 popover 中卵凑,那么控制器 B 不會(huì)調(diào)用該方法,直到控制器 A 清除胜臊。
viewWillLayoutSubviews()
- 該方法在通知控制器將要布局 view 的子控件時(shí)調(diào)用勺卢。
- 每當(dāng)視圖的 bounds 改變,view 將調(diào)整其子控件位置象对。
- 該方法可重寫以在 view 布局子控件前做出改變黑忱。
- 該方法的默認(rèn)實(shí)現(xiàn)為空。
- 該方法調(diào)用時(shí)勒魔,AutoLayout 未起作用甫煞。
- 在控制器生命周期中,該方法可能會(huì)被多次調(diào)用冠绢。
viewDidLayoutSubviews()
- 該方法在通知控制器已經(jīng)布局 view 的子控件時(shí)調(diào)用抚吠。
- 該方法可重寫以在 view 布局子控件后做出改變。
- 該方法的默認(rèn)實(shí)現(xiàn)為空弟胀。
- 該方法調(diào)用時(shí)楷力,AutoLayout 已經(jīng)完成。
- 在控制器生命周期中孵户,該方法可能會(huì)被多次調(diào)用萧朝。
viewDidAppear(_:)
- 該方法在控制器 view 已經(jīng)添加到視圖層次時(shí)被調(diào)用。
- 重寫該方法需要首先調(diào)用父類該方法夏哭。
- 該方法可重寫以進(jìn)行有關(guān)正在展示的視圖操作检柬。
- 在控制器生命周期中,該方法可能會(huì)被多次調(diào)用竖配。
viewWillDisappear(_:)
- 該方法在控制器 view 將要從視圖層次移除時(shí)被調(diào)用何址。
- 類似
viewWillAppear(_:)
。 - 該方法可重寫以提交變更进胯,取消視圖第一響應(yīng)者狀態(tài)头朱。
viewDidDisappear(_:)
- 該方法在控制器 view 已經(jīng)從視圖層次移除時(shí)被調(diào)用。
- 類似
viewDidAppear(_:)
龄减。 - 該方法可重寫以清除或隱藏控件。
didReceiveMemoryWarning()
- 當(dāng)內(nèi)存預(yù)警時(shí)班眯,該方法被調(diào)用希停。
- 不能直接手動(dòng)調(diào)用該方法。
- 該方法可重寫以釋放資源署隘、內(nèi)存宠能。
deinit
- 控制器銷毀時(shí)(離開堆),調(diào)用該方法磁餐。
Note
Rotation
OUTPUT:
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))
- 當(dāng) view 轉(zhuǎn)變违崇,會(huì)調(diào)用
willTransition(to:with:)
方法阿弃。 - 當(dāng)屏幕旋轉(zhuǎn),view 的 bounds 改變羞延,其內(nèi)部的子控件也需要按照約束調(diào)整為新的位置渣淳,因此也調(diào)用了
viewWillLayoutSubviews()
和viewDidLayoutSubviews()
。
Present & Dismiss
OUTPUT:
viewWillDisappear
viewDidDisappear
viewDidDisappear
viewWillAppear
viewDidAppear
- 當(dāng)在一個(gè)控制器內(nèi) Present 新的控制器伴箩,原先的控制器并不會(huì)銷毀入愧,但會(huì)消失,因此調(diào)用了
viewWillDisappear
和viewDidDisappear
方法嗤谚。 - 如果新的控制器 Dismiss棺蛛,即清除自己,原先的控制器會(huì)再一次出現(xiàn)巩步,因此調(diào)用了其中的
viewWillAppear
和viewDidAppear
方法旁赊。
死循環(huán)
class LoopViewController: UIViewController {
override func loadView() {
print(#function)
}
override func viewDidLoad() {
print(#function)
let _ = view
}
}
OUTPUT:
loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()
viewDidLoad()
loadView()
- 若
loadView()
沒有加載 view,viewDidLoad()
會(huì)一直調(diào)用loadView()
加載 view椅野,因此構(gòu)成了死循環(huán)终畅,程序即卡死。