UIViewController的生命周期

最近看了幾篇博客,在這里對ViewConroller的生命周期做一個總結(jié),抽絲剝繭吧,感覺有道理的拿出來匯總一下 。

一、結(jié)構(gòu)

按結(jié)構(gòu)可以對iOS的所有ViewController分成兩類:
1、主要用于展示內(nèi)容的ViewController,這種ViewController主要用于為用戶展示內(nèi)容,并與用戶交互,如UITableViewController,UIViewController。
2、用于控制和顯示其他ViewController的ViewController。這種ViewController一般都是一個ViewController的容器。如UINavigationController,UITabbarController。它們都有一個屬性:viewControllers。其中UINavigationController表示一種Stack式結(jié)構(gòu),push一個ViewController或pop一次,因此后一個ViewController一般會依賴前一個ViewController。而UITabbarController表示一個Array結(jié)構(gòu),各個ViewController是并列的。
第一種ViewController會經(jīng)常被繼承,用來顯示不同的數(shù)據(jù)給用戶。而第二種很少被繼承,除非你真的需要自定義它。

二、ViewController相關(guān)函數(shù)以及執(zhí)行順序
當(dāng)一個視圖控制器被創(chuàng)建,并在屏幕上顯示的時候。 代碼的執(zhí)行順序:**
1、alloc                   創(chuàng)建對象,分配空間
2、init (initWithNibName)  初始化對象,初始化數(shù)據(jù)
3、loadView?????????????   從nib載入視圖 ,通常這一步不需要去干涉。除非你沒有使用xib文件創(chuàng)建視圖
4、viewDidLoad?????????    載入完成,可以進行自定義數(shù)據(jù)以及動態(tài)創(chuàng)建其他控件
5、viewWillAppear???????   視圖將出現(xiàn)在屏幕之前,馬上這個視圖就會被展現(xiàn)在屏幕上了
6、viewDidAppear??????    ?視圖已在屏幕上渲染完成

當(dāng)一個視圖被移除屏幕并且銷毀的時候的執(zhí)行順序,這個順序差不多和上面的相反。**
1、viewWillDisappear?????  視圖將被從屏幕上移除之前執(zhí)行
2、viewDidDisappear????    視圖已經(jīng)被從屏幕上移除,用戶看不到這個視圖了
3、dealloc??????????????  ?視圖被銷毀,此處需要對你在init和viewDidLoad中創(chuàng)建的對象進行釋放

關(guān)于viewDidUnload
在發(fā)生內(nèi)存警告的時候如果本視圖不是當(dāng)前屏幕上正在顯示的視圖的話, viewDidUnload將會被執(zhí)行,本視圖的所有子視圖將被銷毀,以釋放內(nèi)存,此時開發(fā)者需要手動對viewLoad、viewDidLoad中創(chuàng)建的對象釋放內(nèi)存。 因為當(dāng)這個視圖再次顯示在屏幕上的時候,viewLoad、viewDidLoad 再次被調(diào)用,以便再次構(gòu)造視圖。

三、Controller和View的生命周期

這里指的View是指Controller的View。它作為Controler的屬性,生命周期在Controller的生命周期內(nèi)。就是說你的Controller不能在view釋放前就釋放了。


Controller和View的生命周期.png

當(dāng)你alloc并init了一個ViewController時,這個ViewController應(yīng)該是還沒有創(chuàng)建view的。ViewController的view是使用了lazyInit方式創(chuàng)建,就是說你調(diào)用的view屬性的getter:[self view]。在getter里會先判斷view是否創(chuàng)建,如果沒有創(chuàng)建,那么會調(diào)用loadView來創(chuàng)建view。loadView完成時會繼續(xù)調(diào)用viewDidLoad。loadView和viewDidLoad的一個區(qū)別就是:loadView時還沒有view。而viewDidLoad時view以及創(chuàng)建好了。
當(dāng)view被添加其他view中之前時,會調(diào)用viewWillAppear,而之后會調(diào)用viewDidAppear。
當(dāng)view從其他view中移出之前時,會調(diào)用viewWillDisAppear,而之后會調(diào)用viewDidDisappear。
當(dāng)view不在使用,而且是disappeared,受到內(nèi)存警告時,那么viewController會將view釋放并將其指向nil。

四、App運行時的調(diào)用順序

函數(shù)詳解
1)- (void)viewDidLoad;
一個APP在載入時會先通過調(diào)用loadView方法或者載入IB中創(chuàng)建的初始界面的方法,將視圖載入到內(nèi)存中。然后會調(diào)用viewDidLoad方法來進行進一步的設(shè)置。通常,我們對于各種初始數(shù)據(jù)的載入,初始設(shè)定等很多內(nèi)容,都會在這個方法中實現(xiàn),所以這個方法是一個很常用,很重要的方法。
但是要注意,這個方法只會在APP剛開始加載的時候調(diào)用一次,以后都不會再調(diào)用它了,所以只能用來做初始設(shè)置。
2)- (void)viewDidUnload;
在內(nèi)存足夠的情況下,軟件的視圖通常會一直保存在內(nèi)存中,但是如果內(nèi)存不夠,一些沒有正在顯示的viewcontroller就會收到內(nèi)存不夠的警告,然后就會釋放自己擁有的視圖,以達(dá)到釋放內(nèi)存的目的。但是系統(tǒng)只會釋放內(nèi)存,并不會釋放對象的所有權(quán),所以通常我們需要在這里將不需要在內(nèi)存中保留的對象釋放所有權(quán),也就是將其指針置為nil。
這個方法通常并不會在視圖變換的時候被調(diào)用,而只會在系統(tǒng)退出或者收到內(nèi)存警告的時候才會被調(diào)用。但是由于我們需要保證在收到內(nèi)存警告的時候能夠?qū)ζ渥鞒龇磻?yīng),所以這個方法通常我們都需要去實現(xiàn)。
另外,即使在設(shè)備上按了Home鍵之后,系統(tǒng)也不一定會調(diào)用這個方法,因為IOS4之后,系統(tǒng)允許將APP在后臺掛起,并將其繼續(xù)滯留在內(nèi)存中,因此,viewcontroller并不會調(diào)用這個方法來清除內(nèi)存。
3)- (void)viewWillAppear:(BOOL)animated;
系統(tǒng)在載入所有數(shù)據(jù)后,將會在屏幕上顯示視圖,這時會先調(diào)用這個方法。通常我們會利用這個方法,對即將顯示的視圖做進一步的設(shè)置。例如,我們可以利用這個方法來設(shè)置設(shè)備不同方向時該如何顯示。
另外一方面,當(dāng)APP有多個視圖時,在視圖間切換時,并不會再次載入viewDidLoad方法,所以如果在調(diào)入視圖時,需要對數(shù)據(jù)做更新,就只能在這個方法內(nèi)實現(xiàn)了。所以這個方法也非常常用。
4)- (void)viewDidAppear:(BOOL)animated;
有時候,由于一些特殊的原因,我們不能在viewWillApper方法里,對視圖進行更新。那么可以重寫這個方法,在這里對正在顯示的視圖進行進一步的設(shè)置。
5)- (void)viewWillDisappear:(BOOL)animated;
在視圖變換時,當(dāng)前視圖在即將被移除、或者被覆蓋時,會調(diào)用這個方法進行一些善后的處理和設(shè)置。
由于在IOS4之后,系統(tǒng)允許將APP在后臺掛起,所以在按了Home鍵之后,系統(tǒng)并不會調(diào)用這個方法,因為就這個APP本身而言,APP顯示的view,仍是掛起時候的view,所以并不會調(diào)用這個方法。
6)- (void)viewDidDisappear:(BOOL)animated;
我們可以重寫這個方法,對已經(jīng)消失,或者被覆蓋,或者已經(jīng)隱藏了的視圖做一些其他操作。
流程概述

運行APP —> 載入視圖 —> 調(diào)用viewDidLoad方法 —> 調(diào)用viewWillAppear方法 —> viewWillLayoutSubviews —> viewDidLayoutSubviews—> 調(diào)用viewDidAppear方法 —>   正常運行
APP需要調(diào)用另一個view—> 調(diào)用viewWillDisappear—>調(diào)用viewDidDisappear—>收到內(nèi)存警告didReceiveMemoryWarning —>釋放對象所有權(quán)delloc

流程圖

流程圖.png

五、注意

1、init里不要出現(xiàn)創(chuàng)建view的代碼。良好的設(shè)計,在init里應(yīng)該只有相關(guān)數(shù)據(jù)的初始化,而且這些數(shù)據(jù)都是比較關(guān)鍵的數(shù)據(jù)。init里不要掉self.view,否則會導(dǎo)致viewcontroller創(chuàng)建view。(因為view是lazyinit的)。
2、loadView中只初始化view,一般用于創(chuàng)建比較關(guān)鍵的view如tableViewController的tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非關(guān)鍵的view。如果你是從nib文件中創(chuàng)建的viewController在這里一定要首先調(diào)用super的loadView方法,但建議不要重載這個方法。
3、viewDidLoad 這時候view已經(jīng)有了,最適合創(chuàng)建一些附加的view和控件了。有一點需要注意的是,viewDidLoad會調(diào)用多次(viewcontroller可能多次載入view,參見圖2)。
4、viewWillAppear 這個一般在view被添加到superview之前,切換動畫之前調(diào)用。在這里可以進行一些顯示前的處理。比如鍵盤彈出,一些特殊的過程動畫(比如狀態(tài)條和navigationbar顏色)。
5、viewDidAppear 一般用于顯示后,在切換動畫后,如果有需要的操作,可以在這里加入相關(guān)代碼。
6、viewDidUnload 這時候viewController的view已經(jīng)是nil了。由于這一般發(fā)生在內(nèi)存警告時,所以在這里你應(yīng)該將那些不在顯示的view釋放了。比如你在viewcontroller的view上加了一個label,而且這個label是viewcontroller的屬性,那么你要把這個屬性設(shè)置成nil,以免占用不必要的內(nèi)存,而這個label在viewDidLoad時會重新創(chuàng)建。
7、從nib文件加載視圖的controller,只要不釋放,在每次viewWillAppear時都會調(diào)用layoutSubviews方法,有時甚至?xí)趘iewDidAppear后在調(diào)用一次layoutSubviews,而從代碼加載視圖的則只會在開始調(diào)用一次,之后都不會,所以注意,在layoutSubviews中寫相關(guān)的布局代碼十分危險。

六、代碼示例
#pragma mark --- life circle

// 非storyBoard(xib或非xib)都走這個方法
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

    }
    return self;
}

// 如果連接了串聯(lián)圖storyBoard 走這個方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
     NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithCoder:aDecoder]) {

    }
    return self;
}

// 加載視圖(默認(rèn)從nib)
- (void)loadView {
    NSLog(@"%s", __FUNCTION__);
   // [super loadView];
    self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.view.backgroundColor = [UIColor redColor];
}

//視圖控制器中的視圖加載完成,viewController自帶的view加載完成
- (void)viewDidLoad {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidLoad];
}


//視圖將要出現(xiàn)
- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillAppear:animated];
}

// view 即將布局其 Subviews
- (void)viewWillLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillLayoutSubviews];
}

// view 已經(jīng)布局其 Subviews
- (void)viewDidLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidLayoutSubviews];
}

//視圖已經(jīng)出現(xiàn)
- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidAppear:animated];
}

//視圖將要消失
- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillDisappear:animated];
}

//視圖已經(jīng)消失
- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidDisappear:animated];
}

//出現(xiàn)內(nèi)存警告  //模擬內(nèi)存警告:點擊模擬器->hardware-> Simulate Memory Warning
- (void)didReceiveMemoryWarning {
    NSLog(@"%s", __FUNCTION__);
    [super didReceiveMemoryWarning];
}

// 視圖被銷毀
- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}

查看 打印 結(jié)果
2017-04-24 00:16:32.099 ViewController[16899:41923212] -[ViewController initWithCoder:]
2017-04-24 00:16:32.101 ViewController[16899:41923212] -[ViewController loadView]
2017-04-24 00:16:32.102 ViewController[16899:41923212] -[ViewController viewDidLoad]
2017-04-24 00:16:32.102 ViewController[16899:41923212] -[ViewController viewWillAppear:]
2017-04-24 00:16:32.104 ViewController[16899:41923212] -[ViewController viewWillLayoutSubviews]
2017-04-24 00:16:32.104 ViewController[16899:41923212] -[ViewController viewDidLayoutSubviews]
2017-04-24 00:16:32.104 ViewController[16899:41923212] -[ViewController viewWillLayoutSubviews]
2017-04-24 00:16:32.104 ViewController[16899:41923212] -[ViewController viewDidLayoutSubviews]
2017-04-24 00:16:32.106 ViewController[16899:41923212] -[ViewController viewDidAppear:]

分析

  1. initWithNibName:bundle:
    初始化UIViewController,執(zhí)行關(guān)鍵數(shù)據(jù)初始化操作,非StoryBoard創(chuàng)建UIViewController都會調(diào)用這個方法。
    注意: 不要在這里做View相關(guān)操作,View在loadView方法中才初始化。
  1. initWithCoder:
    如果使用StoryBoard進行視圖管理,程序不會直接初始化一個UIViewController,StoryBoard會自動初始化或在segue被觸發(fā)時自動初始化,因此方法initWithNibName:bundle不會被調(diào)用,但是initWithCoder會被調(diào)用。
  1. loadView
    當(dāng)訪問UIViewController的View屬性時,View如果此時為nil,那么ViewController會自動調(diào)用loadView方法來初始化一個UIView并賦值給UIViewController的View;如果沒有重載lodaView方法,則UIViewController會從nib或StoryBoard中查找默認(rèn)的loadView,默認(rèn)的loadView會返回一個空白的UIView對象。
    注意:在view初始化之前,不能先調(diào)用view的getter方法,否則將導(dǎo)致死循環(huán)(除非先調(diào)用[super loadView])
  1. viewDidLoad
    當(dāng)loadView將view載入內(nèi)存中,會進一步調(diào)用viewDidLoad方法來進行進一步設(shè)置。通常,我們對于各種初始化數(shù)據(jù)的載入,初始設(shè)定等很多內(nèi)容都會在這個方法中實現(xiàn)。
  1. viewWillAppear
    系統(tǒng)在載入所有的數(shù)據(jù)后,將會在屏幕上顯示視圖,這時會先調(diào)用這個方法,通常我們會在這個方法對即將顯示的視圖做進一步的設(shè)置。比如,設(shè)置設(shè)備不同方向時該如何顯示;設(shè)置狀態(tài)欄方向、設(shè)置視圖顯示樣式等。
    另一方面,當(dāng)APP有多個視圖時,上下級視圖切換是也會調(diào)用這個方法,如果在調(diào)入視圖時,需要對數(shù)據(jù)做更新,就只能在這個方法內(nèi)實現(xiàn)。
  1. viewWillLayoutSubviews
    view 即將布局其Subviews。 比如view的bounds改變了(例如:狀態(tài)欄從不顯示到顯示,視圖方向變化),要調(diào)整Subviews的位置,在調(diào)整之前要做的工作可以放在該方法中實現(xiàn)
  1. viewDidLayoutSubviews
    view已經(jīng)布局其Subviews,這里可以放置調(diào)整完成之后需要做的工作。
  1. viewDidAppear
    在view被添加到視圖層級中以及多視圖,上下級視圖切換時調(diào)用這個方法,在這里可以對正在顯示的視圖做進一步的設(shè)置。
  1. viewWillDisappear
    在視圖切換是,當(dāng)前視圖在即將被移除、或被覆蓋是,會調(diào)用該方法,此時還沒有調(diào)用removeFromSuperview。
  1. viewDidDisappear
    view已經(jīng)消失或被覆蓋,此時已經(jīng)調(diào)用removeFromSuperView;
  1. dealloc
    視圖被銷毀,此次需要對你在init和viewDidLoad中創(chuàng)建的對象進行釋放。
  1. didReceiveMemoryWarning
    在內(nèi)存足夠的情況下,app的視圖通常會一直保存在內(nèi)存中,但是如果內(nèi)存不夠,一些沒有正在顯示的viewController就會收到內(nèi)存不夠的警告,然后就會釋放自己擁有的視圖,以達(dá)到釋放內(nèi)存的目的。但是系統(tǒng)只會釋放內(nèi)存,并不會釋放對象的所有權(quán),所以通常我們需要在這里將不需要顯示在內(nèi)存中保留的對象釋放它的所有權(quán),將其指針置nil。

------整理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容