探究 UIViewController 生命周期

由于種種原因铭污,簡(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 查看鹿寨、下載。

Structure

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)用了 viewWillDisappearviewDidDisappear 方法嗤谚。
  • 如果新的控制器 Dismiss棺蛛,即清除自己,原先的控制器會(huì)再一次出現(xiàn)巩步,因此調(diào)用了其中的 viewWillAppearviewDidAppear 方法旁赊。

死循環(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)终畅,程序即卡死。

Reference

也歡迎您關(guān)注我的微博 @萌面大道V & 簡(jiǎn)書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鳄橘,一起剝皮案震驚了整個(gè)濱河市声离,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瘫怜,老刑警劉巖术徊,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鲸湃,居然都是意外死亡赠涮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門暗挑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笋除,“玉大人,你說我怎么就攤上這事炸裆±” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵烹看,是天一觀的道長(zhǎng)国拇。 經(jīng)常有香客問我,道長(zhǎng)惯殊,這世上最難降的妖魔是什么酱吝? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮土思,結(jié)果婚禮上务热,老公的妹妹穿的比我還像新娘忆嗜。我一直安慰自己,他們只是感情好崎岂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布捆毫。 她就那樣靜靜地躺著,像睡著了一般该镣。 火紅的嫁衣襯著肌膚如雪冻璃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天损合,我揣著相機(jī)與錄音省艳,去河邊找鬼。 笑死嫁审,一個(gè)胖子當(dāng)著我的面吹牛跋炕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播律适,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼辐烂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捂贿?” 一聲冷哼從身側(cè)響起纠修,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厂僧,沒想到半個(gè)月后扣草,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜屠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年辰妙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甫窟。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡密浑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粗井,到底是詐尸還是另有隱情尔破,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布浇衬,位于F島的核電站懒构,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏径玖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一颤介、第九天 我趴在偏房一處隱蔽的房頂上張望梳星。 院中可真熱鬧赞赖,春花似錦、人聲如沸冤灾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽韵吨。三九已至匿垄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間归粉,已是汗流浹背椿疗。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糠悼,地道東北人届榄。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像倔喂,于是被迫代替她去往敵國(guó)和親铝条。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容