天天看點

iOS對UIViewController生命周期和屬性方法的解析(一)iOS對UIViewController生命周期和屬性方法的解析

iOS對UIViewController生命周期和屬性方法的解析

一、引言

       作為MVC設計模式中的C,Controller一直扮演着項目開發中最重要的角色,它是視圖和資料的橋梁,通過它的管理,将資料有條有理的展示在我們的View層上。iOS中的UIViewController是UIKit架構中最基本的一個類。從第一個UI視圖到複雜完整項目,都離不開UIViewController作為基礎。基于UIViewController的封裝和擴充,也能夠出色的完成各種複雜界面邏輯。這篇部落格,旨在讨論UIViewController的生命周期和屬性方法,在最基礎的東西上,往往會得到意想不到的驚喜。

二、UIViewController的生命周期

       要了解UIViewController,先要弄清楚其生命周期。在面向對象的語言中,是對象,就一定要有生命周期,UIViewController也不例外,生命周期管理Controller的作用範圍和時間,也管理其内對象的作用範圍和時間。首先,UIViewController中與其生命周期有關的幾個函數如下:

//類的初始化方法

+ (void)initialize;

//對象初始化方法

- (instancetype)init;

//從歸檔初始化

- (instancetype)initWithCoder:(NSCoder *)coder;

//加載視圖

-(void)loadView;

//将要加載視圖

- (void)viewDidLoad;

//将要布局子視圖

-(void)viewWillLayoutSubviews;

//已經布局子視圖

-(void)viewDidLayoutSubviews;

//記憶體警告

- (void)didReceiveMemoryWarning;

//已經展示

-(void)viewDidAppear:(BOOL)animated;

//将要展示

-(void)viewWillAppear:(BOOL)animated;

//将要消失

-(void)viewWillDisappear:(BOOL)animated;

//已經消失

-(void)viewDidDisappear:(BOOL)animated;

//被釋放

-(void)dealloc;

上面這麼多的函數,乍一看什麼複雜,其實關系什麼明朗,除了initialize,init和initWithCoder不是存在所有對象的聲明周期中,其他函數都會在UIViewController的聲明周期中有序的被調用。那麼具體的調用順序是怎樣的呢,最好的辦法是實踐一下,通過編号列印,結果如下:

這是一個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中寫相關的布局代碼十分危險。

三、從storyBoard加載UIViewController執行個體的傳值陷阱

       我們知道,當我們從StoryBoard中加載ViewController時,我們在Controller中拖拽的視圖是可以被初始化的,這裡面有一點需要我們注意,如果我們需要向controller中視圖進行傳值設定,通過以下方法得到的Controller中,視圖還沒有被初始化建立出來:

ViewController2 * viewController2 = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"ViewController2"];

我們可以在ViewController2的storyBoard中拉一個label,然後關聯到頭檔案中,如下列印,會發現我們得到controller時,裡面的視圖對象并沒有進行建立:

ViewController2 * viewController2 = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"ViewController2"];

   NSLog(@"%@",viewController2.label);

   [self presentViewController:viewController2 animated:YES completion:nil];

列印如下:

iOS對UIViewController生命周期和屬性方法的解析(一)iOS對UIViewController生命周期和屬性方法的解析

可以想象,如果我們這時候需要對label進行一些屬性設定,必然失敗。有人提出可以在建立後,手動調以下loadView方法,我們試一下,結果如下:

iOS對UIViewController生命周期和屬性方法的解析(一)iOS對UIViewController生命周期和屬性方法的解析

可以看到,手動調用loadView後,label是被建立了出來,但是暴漏了一個更嚴重的問題,系統不在調用ViewDidLoad方法,這是十分有風險的,因為我們大部分的初始化代碼都會放在這個方法裡,是以手動調用loadView是一種錯誤的方法,apple文檔聲明對于loadView方法,我們從來都不要手動直接調用,那麼我們如何實作建立後對成員對象進行傳值設定呢,iOS9中增加了這樣一個方法:

- (void)loadViewIfNeeded NS_AVAILABLE_IOS(9_0);

這個方法十分有用,調用這個方法,會将視圖建立出來,并且不會忽略viewDidLoad的調用。

在iOS9中,UIViewController還增加了下面一個布爾值的屬性,可以同來判斷controller的view是否已經加載完成:

@property(nullable, nonatomic, readonly, strong) UIView *viewIfLoaded NS_AVAILABLE_IOS(9_0);

繼續閱讀