引言:
ViewController是iOS開發(fā)中MVC模式中的C(視圖控制器),ViewController是view的controller,ViewController的職責(zé)主要包括管理內(nèi)部各個view的加載顯示和卸載,同時負(fù)責(zé)與其他ViewController的通信和協(xié)調(diào)。
I. 簡介
在IOS中,有兩類ViewController:
用于展示內(nèi)容:
比如UIViewController、UITableViewController等,同時還可以自定義繼承自UIViewController的UIViewController;
ViewController容器:
比如,UINavigationViewController和UITabBarController等,UINavigationController是以Stack的形式來存儲和管理ViewController,UITabBarController是以Array的形式來管理ViewController。
不管是哪類ViewController,都繼承自UIViewController
II. UIViewController
控制器從創(chuàng)建到銷毀方法的執(zhí)行順序
load->
initialize->
init(initWithNibName)—>
loadView—>
viewDidLoad—>
viewWillApper—>
viewDidApper—>
viewWillDisapper—>
viewDidDisapper—>
viewWillUnload->
viewDidUnload—>
dealloc
其中viewWillUnload跟viewDidUnLoad在iOS6以后就過期了. 收到low-memory時系統(tǒng)不會釋放view,而只是釋放controller的resource`。
注意點:
LoadView:
- 控制器調(diào)用loadView方法創(chuàng)建控制器的view.當(dāng)控制器的view存在了就不會調(diào)用.
- 不要再LoadView中調(diào)用
[super loadView],會影響CPU性能
蘋果官方文檔:
You should never call this method directly. The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property.
當(dāng)系統(tǒng)要展示這個控制器view的時候,會先去view的getter方法中尋找有沒有返回view,如果
view == nil,系統(tǒng)就會主動去調(diào)用這個方法.
控制器的view都是懶加載,當(dāng)需要展示的時候才會去創(chuàng)建
- 懶加載:重寫getter方法
- 好處:不用管什么時候需要創(chuàng)建,做到要用時再創(chuàng)建
III. view的加載過程
這里指的View是指Controller的View。它作為Controler的屬性,生命周期在Controller的生命周期內(nèi)。就是說你的Controller不能在view釋放前就釋放了。

從代碼中加載view

從storyboard/xib中創(chuàng)建view

實現(xiàn)過程

方法調(diào)用順序
+ (id)alloc
分配內(nèi)存;
- (id)init 方法(包括其他-(id)init...方法)
只允許調(diào)用一次,并且要與 alloc方法 寫在一起,在init方法中申請的內(nèi)存,要在dealloc方法中釋放(或者其他地方);
- (void)awakeFromNib
使用Xib初始化后會調(diào)用此方法,一般不會重寫此方法;
- (void)loadView
如果使用Xib創(chuàng)建ViewController,就不要重寫該方法。一般不會修改此方法;
- (void)viewDidLoad
視圖加載完成之后被調(diào)用,這個方法很重要,可以在此增加一些自己定義的控件,注意此時view的frame不一定是顯示時候的frame,真實的frame會在 - (void)viewDidAppear: 后。
在iOS6.0+版本中在對象的整個生命周期中只會被調(diào)用一次,這里要注意在iOS3.0~iOS5.X版本中可能會被重復(fù)調(diào)用,當(dāng)ViewController收到內(nèi)存警告后,會釋放View,并調(diào)用viewDidUnload,之后會重新調(diào)用viewDidLoad,所以要支持iOS6.0以前版本的童鞋要注意這里的內(nèi)存管理。
- (void)viewWillAppear:(BOOL)animated
view 將要顯示的時候,可以在此加載一些圖片,和一些其他占內(nèi)存的資源;
- (void)viewDidAppear:(BOOL)animated
view 已經(jīng)顯示的時候調(diào)用;
- (void)viewWillDisappear:(BOOL)animated
view 將要隱藏的時候,可以在此將一些占用內(nèi)存比較大的資源先釋放掉,在 viewWillAppear: 中重新加載。如:圖片/聲音/視頻。如果View已經(jīng)隱藏而又在內(nèi)存中保留這些在顯示前不會被調(diào)用的資源,那么App閃退的幾率會增加,尤其是ViewController比較多的時候;
- (void)viewDidAppear:(BOOL)animated
view 已經(jīng)隱藏的時候調(diào)用;
- (void)dealloc
不要手動調(diào)用此方法,當(dāng)引用計數(shù)值為0的時候,系統(tǒng)會自動調(diào)用此方法。
IV. 類相關(guān)方法
+(void)load
當(dāng)一個類被加載時調(diào)用,只加載一次
+(void)initialize
當(dāng)本類或者子類被加載時調(diào)用,可能調(diào)用多次
-(instancetype)init
用代碼創(chuàng)建類的時候調(diào)用,只能做一些初始化操作,不能設(shè)置控件的frame,init其實是去調(diào)用initWithFrame,只不過frame為CGRectZero
-(instancetype)initWithFrame:(CGRect)frame
用代碼創(chuàng)建類的時候調(diào)用,只能做一些初始化操作,不能在這設(shè)置控件的frame,如果已經(jīng)知道了frame,那么在這里設(shè)置子控件的frame是沒有問題的,但是如果外界使用init的方式創(chuàng)建,最終也會調(diào)用initWithFrame方法,此時的frame傳進(jìn)來是0,那么,在這個方法里面設(shè)置的子控件的frame也會為0.所以,為了嚴(yán)謹(jǐn)起見,最好不要在這個方法里面設(shè)置子控件的frame。
-(instancetype)initWithCoder:(NSCoder *)aDecoder
從xib/storyboard中加載就會調(diào)用此方法,只能在這個方法做一些一次性設(shè)置,不能設(shè)置控件的frame
-(void)awakeFromNib
從文件中加載.就會調(diào)用此方法,可以在這個方法中設(shè)置frame
-(void)layoutSubviews
如果你想改變子視圖的默認(rèn)布局時才需要去重寫 layoutSubviews 方法。
使用 NavigationController 去 Push 切換顯示的View, 調(diào)用的順序
例如 從 A 控制器 Push 顯示 B 控制器,
[self.navigationController pushViewController:B animated:YES]
1. 加載B控制器的View(如果沒有的話);
2. 調(diào)用 A 的 - (void)viewWillDisappear:(BOOL)animated; A將要隱藏
3. 調(diào)用 B 的 - (void)viewWillAppear:(BOOL)animated; B將要顯示
4. 調(diào)用 A 的 - (void)viewDidDisappear:(BOOL)animated;A已經(jīng)隱藏
5. 調(diào)用 B 的 - (void)viewDidAppear:(BOOL)animated; B已經(jīng)顯示
V. 收到內(nèi)存警告系統(tǒng)執(zhí)行步驟
- (void)didReceiveMemoryWarning
iOS6.0以后 內(nèi)存不夠用時,會調(diào)用這個方法,接收到內(nèi)存警告.
