iOS ViewController的生命周期

? ? ? ? ViewController是IOS開發中MVC模式中的C,ViewController是view的controller,ViewController的職責主要包括管理內部各個view的加載顯示和卸載,同時負責與其他ViewController的通信和協調。在IOS中,有兩類ViewController,一類是顯示內容的,比如UIViewController、UITableViewController等,同時還可以自定義繼承自UIViewController的ViewController;另一類是ViewController容器,UINavigationViewController和UITabBarController等,UINavigationController是以Stack的形式來存儲和管理ViewController,UITabBarController是以Array的形式來管理ViewController。和Android中Activity一樣,IOS開發中,ViewController也有自己的生命周期(Lifecycle)。

首先來看看View的加載過程,如下圖:


從圖中可以看到,在view加載過程中首先會調用loadView方法,在這個方法中主要完成一些關鍵view的初始化工作,比如UINavigationViewController和UITabBarController等容器類的ViewController;接下來就是加載view,加載成功后,會接著調用viewDidLoad方法,這里要記住的一點是,在loadView之前,是沒有view的,也就是說,在這之前,view還沒有被初始化。完成viewDidLoad方法后,ViewController里面就成功的加載view了,如上圖右下角所示。

在Controller中創建view有兩種方式,一種是通過代碼創建、一種是通過Storyboard或Interface Builder來創建,后者可以比較直觀的配置view的外觀和屬性,Storyboard配合IOS6后推出的AutoLayout。通過IB或Storyboard創建view,在Controller中創建view后,會在Controller中對view進行一些操作,會出現如下代碼:

@interface MyViewController()

@property?(nonatomic)?IBOutlet?id?myButton;

@property?(nonatomic)?IBOutlet?id?myTextField;

-?(IBAction)myAction:(id)sender;

@end

這里用IBOutlet標記了一個UIButton和一個UITextField,用IBAction來標記UIButton的響應事件,IBOutlet和IBAction都是一個整形常量,用來標記控件,通過一張圖能比較清晰的看清他們之間的關系:


從圖中可以看到,當系統發出內存警告時,會調用didReceiveMemoeryWarning方法,如果當前有能被釋放的view,系統會調用viewWillUnload方法來釋放view,完成后調用viewDidUnload方法,至此,view就被卸載了。此時原本指向view的變量要被置為nil,具體操作是在viewDidUnload方法中調用self.myButton = nil。

上圖中,MyViewController是繼承自UIViewController的一個自定義ViewController,它包含兩個View,一個是UIButton,一個是UITextField,從箭頭的指向性上就可以比較好的理解IBOutlet和IBAction了。IBOutlet是告訴Interface Builder,此實例變量被連接到nib文件中的view對象,IBOutlet本身不做任何操作,只是一個標記作用。IBAction同樣是個標記關鍵字,它只能標記方法,它告訴IB用IBAction標記的方法可以被某個控件觸發。

通過代碼的方式創建view,如下代碼:

- (void)loadView

{

? ? ?CGRect?applicationFrame?=?[[UIScreen?mainScreen]?applicationFrame];

? ? ?UIView?*contentView?=?[[UIView?alloc]?initWithFrame:applicationFrame];

? ? ?contentView.backgroundColor?=?[UIColor?redColor];

? ? ?self.view?=?contentView;

? ? ?levelView?=?[[LevelView?alloc]?initWithFrame:applicationFrame?viewController:self];

? ? ?[self.view?addSubview:levelView];

}

上述代碼首先得到屏幕的frame,然后根據該frame生成了一個contentView,并指定當前ViewController的root view為contentView,然后生成了一個LevelView的自定義View并將它通過addSubview:方法添加到當前ViewController當中,完成view的初始化加載。

關于loadView方法的重寫,官方文檔中有一個明顯的注釋,原文如下:

Note:When overriding theloadViewmethod to create your views programmatically, you should not callsuper. Doing so initiates the default view-loading behavior and usually just wastes CPU cycles. Your own implementation of theloadViewmethod should do all the work that is needed to create a root view and subviews for your view controller.

意思是當通過代碼方式去創建你自己的view時,在loadView方法中不應該調用super,如果調用[super loadView]會影響CPU性能。

接下來我們看看ViewController中的view是如何被卸載的:


從圖中可以看到,當系統發出內存警告時,會調用didReceiveMemoeryWarning方法,如果當前有能被釋放的view,系統會調用viewWillUnload方法來釋放view,完成后調用viewDidUnload方法,至此,view就被卸載了。此時原本指向view的變量要被置為nil,具體操作是在viewDidUnload方法中調用self.myButton = nil.

注意:viewWillUnload和viewDidUnload已經在ios6被廢棄了,因為清除視圖的引用已經沒有必要了(Clearing references to views is no longer necessary)

另外需要注意的是:

1.沒有viewWillLoad。

2.viewDidLoad和viewDidUnload并不是成對的。


這是一個ViewController完整的聲明周期,其實里面還有好多地方需要我們注意一下

1:initialize函數并不會每次創建對象都調用,只有在這個類第一次創建對象時才會調用,做一些類的準備工作,再次創建這個類的對象,initalize方法將不會被調用,對于這個類的子類,如果實現了initialize方法,在這個子類第一次創建對象時會調用自己的initalize方法,之后不會調用,如果沒有實現,那么它的父類將替它再次調用一下自己的initialize方法,以后創建也都不會再調用。因此,如果我們有一些和這個相關的全局變量,可以在這里進行初始化。

2:init方法和initCoder方法相似,只是被調用的環境不一樣,如果用代碼進行初始化,會調用init,從nib文件或者歸檔進行初始化,會調用initCoder。

3:loadView方法是開始加載視圖的起始方法,除非手動調用,否則在ViewController的生命周期中沒特殊情況只會被調用一次。

4:viewDidLoad方法是我們最常用的方法的,類中成員對象和變量的初始化我們都會放在這個方法中,在類創建后,無論視圖的展現或消失,這個方法也是只會在將要布局時調用一次。

5:viewWillAppear:視圖將要展現時會調用。

6:viewWillLayoutSubviews:在viewWillAppear后調用,將要對子視圖進行布局。

7:viewDidLayoutSubviews:已經布局完成子視圖。

8:viewDidAppare:視圖完成顯示時調用。

9:viewWillDisappear:視圖將要消失時調用。

10:viewDidDisappear:視圖已經消失時調用。

11:dealloc:controller被釋放時調用。

注意:經過測試,從nib文件加載的controller,只要不釋放,在每次viewWillAppare時都會調用layoutSubviews方法,有時甚至會在viewDidAppare后在調用一次layoutSubviews,而重點是從代碼加載的則只會在開始調用一次,之后都不會,所以注意,在layoutSubviews中寫相關的布局代碼十分危險。

ViewController的生命周期中各方法執行流程如下:

alloc -> init -> loadView -> viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisappear -> viewDidDisappear -> dealloc


viewWillUnload和viewDidUnload

viewWillUnload:(iOS6廢除)

1.當消除掉控制器的視圖之前調用

2.視圖不會再在低內存條件下被清除所以這個方法不會再被調用。

3.在iOS5之前,當低的內存情況發生的時候,當前控制器的視圖們不再被需要的時候,系統會有選擇性的將這些視圖從這些內存移除。這個方法被調用的目的是在視圖被真正的銷毀前你可以執行一些清空的任務。比如,你想要使用這個方法去清空視圖的觀察者或通知或者記錄視圖的狀態以便當重新讀取的時候恢復。

4.在iOS6之后,不再需要清空視圖的引用。因此,其他一些關于清理的方法,比如清空觀察者,也不是必要的了。

viewDidUnload:(iOS6廢除)

1.當視圖從內存中被消除后調用

2.視圖在低的內存情況下不再被銷毀所以這個方法也不再會調用

3.在iOS5之前,低的內存狀況發生之后,當前的試圖控制器的視圖不再被需要,系統會選擇性的在視圖控制器的視圖被銷毀后調用。這個方法使你執行一些最后清空任務的最后機會如果你的視圖控制器存儲著對視圖的或子控件的單獨引用,你應該使用這個方法去釋放這些引用。你也可以使用這個方法去消除一些你再創建視圖時候所創建的但是當視圖不再的時候不再需要的關于對象的引用。你不應該使用這個方法去釋放用戶數據或者一些不能輕易被重新創建的數據。

4.在iOS6之后,清空在試圖控制器中對views和其他對象的引用已經沒有必要了。

5.當這個方法被調用的時候,視圖的屬性是空的。


參考:http://www.lxweimin.com/p/fcfbd4919b0b

http://blog.csdn.net/ryantang03/article/details/8264072

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容