當一個視圖控制器被創(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 視圖已在屏幕上渲染完成
當一個視圖被移除屏幕并且銷毀的時候的執(zhí)行順序,這個順序差不多和上面的相反
1、viewWillDisappear 視圖將被從屏幕上移除之前執(zhí)行
2、viewDidDisappear 視圖已經(jīng)被從屏幕上移除,用戶看不到這個視圖了
3、dealloc 視圖被銷毀,此處需要對你在init和viewDidLoad中創(chuàng)建的對象進行釋放
關于viewDidUnload :在發(fā)生內(nèi)存警告的時候如果本視圖不是當前屏幕上正在顯示的視圖的話, viewDidUnload將會被執(zhí)行,本視圖的所有子視圖將被銷毀,以釋放內(nèi)存,此時開發(fā)者需要手動對viewLoad、viewDidLoad中創(chuàng)建 的對象釋放內(nèi)存。 因為當這個視圖再次顯示在屏幕上的時候,viewLoad、viewDidLoad 再次被調(diào)用,以便再次構(gòu)造視圖。
二、view的加載過程
文字說明在表述流程的時候總是很費力的,我又找到了如下的兩張圖
跟隨如下文字理解viewController對view加載過程:
1 先判斷子類是否重寫了loadView,如果有直接調(diào)用。之后調(diào)viewDidLoad完成View的加載。
2 如果是外部通過調(diào)用initWithNibName:bundle指定nib文件名的話,ViewController記載此nib來創(chuàng)建View。
3 如果initWithNibName:bundle的name參數(shù)為nil,則ViewController會通過以下兩個步驟找到與其關聯(lián)的nib。
A 如果類名包含Controller,例如ViewController的類名是MyViewController,則查找是否存在MyView.nib;
B 找跟ViewController類名一樣的文件,例如MyViewController,則查找是否存在MyViewController.nib。
4 如果子類沒有重寫的loadView,則ViewController會從StroyBoards中找或者調(diào)用其默認的loadView,默認的loadView返回一個空白的UIView對象。
注意第一步,ViewController是判斷子類是否重寫了loadView,而不是判斷調(diào)用子類的loadView之后 ViewController的View是否為空。就是說,如果子類重寫了loadView的話,不管子類在loadView里面能否獲取到 View,ViewController都會直接調(diào)viewDidLoad完成View的加載。
三、view卸載過程圖
跟隨以下文字理解卸載過程:
1 系統(tǒng)發(fā)出警告或者ViewController本身調(diào)用導致didReceiveMemoryWarning被調(diào)用
2 調(diào)用viewWillUnload之后釋放View
3 調(diào)用viewDidUnload
四、模擬器的調(diào)用順序
我構(gòu)架了這樣一個環(huán)境,在該環(huán)境中有兩個viewController,姑且命名為A和B,tag分別為1和2,A控制程序啟動的時候即加載的界面,在A中放一個按鈕,按下后會通過segue來調(diào)用到界面B;B 中頁放一個按鈕,通過執(zhí)行
[self dismissModalViewControllerAnimated:YES];
來返回界面A
然后檢測所有的函數(shù)調(diào)用,依次如下
加載A的時候依次調(diào)用
1 initWithCoder
1 loadView //如果說你進行了重寫,會在這里調(diào)用,這一步可以參考下文
1 viewDidLoad
1 viewWillAppear
1 viewWillLayoutSubviews
1 viewDidLayoutSubviews
1 viewDidAppear
切換至B的時候依次調(diào)用
2 initWithCoder //先將2初始化
1 prepareForSegue //調(diào)用1的準備過度的函數(shù),所以在該函數(shù)中可以對界面B的一些相關屬性進行賦值
2 loadView //如果這里進行了重寫
2 viewDidLoad //2界面加載
1 viewWillDisappear
2 viewWillAppear
2 viewWillLayoutSubviews
2 viewDidLayoutSubviews
2 viewDidAppear
1 viewDidDisappear
從B切換回A的時候依次調(diào)用
2 viewWillDisappear
1 viewWillAppear
1 viewDidAppear
2 viewDidDisappear
2 dealloc
順序總結(jié)下來加載依次為:加載 - 顯示 - 布局
完成順序依次為:完成布局 - 完成顯示 - 完成加載
小注:-(void)loadView;函數(shù)如果重寫,下面是一個可能的demo
-(void)loadView
{
CGRect applicationFrame = [[UIScreenmainScreen] applicationFrame];
UIView *contentView = [[UIViewalloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColordarkGrayColor];
self.view = contentView;
UILabel *lab = [[UILabelalloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
lab.text = @"HelloWorld";
[self.viewaddSubview:lab];
}
loadView雖然返回值為空,但必須在函數(shù)體內(nèi)對self.view進行賦值,否則會在建立該界面的時候收到如下的log信息:
Application windows are expected to have a root view controller at the end of application launch
具體執(zhí)行順序為:代碼執(zhí)行了initWithCoder之后直接調(diào)用了三次loadView函數(shù),并且沒有調(diào)用其它函數(shù)(包括viewDidLoad 、viewWillDisappear、viewWillLayoutSubviews)
疑問:
暫不清楚為什么會調(diào)用三次,我的猜測是:上述三個函數(shù)分別檢測了一遍view是否存在,發(fā)現(xiàn)不存在,所以各自調(diào)用了一遍viewLoad,最后發(fā)現(xiàn)依然不存在,所以上述三個函數(shù)分別返回了失敗,加載完成
但矛盾的地方是:為什么上述三個函數(shù)本身沒有執(zhí)行到?底層到底做了什么?
五、view和ViewController的創(chuàng)建階段,關于什么時候應該干什么
1、init
Allocating critical data structures required by your view controller
不要出現(xiàn)創(chuàng)建view的代碼。良好的設計,在init里應該只有相關數(shù)據(jù)的初始化,而且這些數(shù)據(jù)都是比較關鍵的數(shù)據(jù)。init里不要掉self.view,否則會導致viewcontroller創(chuàng)建view。(因為view是lazyinit的)。
2、loadView
Creating your view objects
只初始化view,一般用于創(chuàng)建比較關鍵的view如tableViewController的 tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非關鍵的view。如果你是從nib文件中創(chuàng)建的viewController在這里一定要首先調(diào)用 super的loadView方法,但建議不要重載這個方法。
3、viewDidLoad
Allocating or loading data to be displayed in your view
這時候view已經(jīng)有了,最適合創(chuàng)建一些附加的view和控件了。有一點需要注意的是,viewDidLoad會調(diào)用多次(viewcontroller可能多次載入view,參見圖2)。
4、viewWillAppear 這個一般在view被添加到superview之前,切換動畫之前調(diào)用。在這里可以進行一些顯示前的處理。比如鍵盤彈出,一些特殊的過程動畫(比如狀態(tài)條和navigationbar顏色)。
5、viewDidAppear 一般用于顯示后,在切換動畫后,如果有需要的操作,可以在這里加入相關代碼。
6、viewDidUnload
Releasing references to view objects
Releasing data that is not needed when your view is not displayed
這時候viewController的view已經(jīng)是nil了。由于這一般發(fā)生在內(nèi)存警告時,所以在這里你應該將那些不在顯示的view釋放了。比 如你在viewcontroller的view上加了一個label,而且這個label是viewcontroller的屬性,那么你要把這個屬性設置 成nil,以免占用不必要的內(nèi)存,而這個label在viewDidLoad時會重新創(chuàng)建。
7、dealloc
Releasing critical data structures required by your view controller
六、幾點備注:
1、按結(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ù)給用戶。而第二種很少被繼承,除非你真的需要自定義它。
2、當view被添加其他view中之前時,會調(diào)用viewWillAppear,而之后會調(diào)用viewDidAppear。
當view從其他view中移出之前時,會調(diào)用viewWillDisAppear,而之后會調(diào)用viewDidDisappear。
當view不在使用,而且是disappeared,受到內(nèi)存警告時,那么viewController會將view釋放并將其指向nil。
3、由于Controller加載View時,會自動將一些View對象指向其對應的IBOutlet變量。
所以當view被卸載時我們必須在viewDidUnload將這些變量release掉,ViewController不會自動做這件事。
具體做法是將變量設置為空,(注意和dealloc中將變量release的區(qū)別)注意此時Controller的view屬性是空的。