- iOS控制器平時總調用
viewDidload
其流程也是從viewDidLoad
->viewWillAppear
->viewDidAppear
->viewWillDisappear
->viewDidDisappear
基本的流程就這樣了,那還有沒有呢???答案是當然有
具體是什么呢?下面具體來研究下,小知識也能翻船?? - 先看看API吧??
- (void)loadView; // This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly.
- (void)loadViewIfNeeded NS_AVAILABLE_IOS(9_0); // Loads the view controller's view if it has not already been set.
- (void)viewWillUnload NS_DEPRECATED_IOS(5_0,6_0) __TVOS_PROHIBITED;
- (void)viewDidUnload NS_DEPRECATED_IOS(3_0,6_0) __TVOS_PROHIBITED; // Called after the view controller's view is released and set to nil. For example, a memory warning which causes the view to be purged. Not invoked as a result of -dealloc.
- (void)viewDidLoad; // Called after the view has been loaded. For view controllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.
- (BOOL)isViewLoaded NS_AVAILABLE_IOS(3_0);
- (void)viewWillAppear:(BOOL)animated; // Called when the view is about to made visible. Default does nothing
- (void)viewDidAppear:(BOOL)animated; // Called when the view has been fully transitioned onto the screen. Default does nothing
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
// Called just before the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewWillLayoutSubviews NS_AVAILABLE_IOS(5_0);
// Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewDidLayoutSubviews NS_AVAILABLE_IOS(5_0);
- (void)didReceiveMemoryWarning; // Called when the parent application receives a memory warning. On iOS 6.0 it will no longer clear the view by default.
看到這里很慚愧,里面很多方法平時很少用,那么具體的流程到底是怎樣的呢?
自己寫執行代碼打印輸入如下:
從A到B
viewDidLoad-----A
viewWillAppear-----A
viewWillLayoutSubviews-----A
viewDidLayoutSubviews-----A
viewDidAppear-----A
viewDidLoad-----B
viewWillDisappear-----A
viewWillAppear-----B
viewWillLayoutSubviews-----B
viewDidLayoutSubviews-----B
viewDidDisappear-----A
viewDidAppear-----B
- 從
A
到B
的過程中,B
加載后(即viewDidLoad->B
)此時A
將會消失,等到B
視圖出現之前A
徹底視圖消失
從B到A
viewWillDisappear-----B
viewWillAppear-----A
viewDidDisappear-----B
viewDidAppear-----A
- 從
B
到A
的過程中,首先B
視圖將會消失,等到A
視圖徹底出現之前,B
視圖才會徹底消失. - 再從
A
到B
viewDidLoad-----B
viewWillDisappear-----A
viewWillAppear-----B
viewWillLayoutSubviews-----B
viewDidLayoutSubviews-----B
viewDidDisappear-----A
viewDidAppear-----B
- loadView:每次訪問view時,就會調用self.view的get方法,在get方法中判斷self.view==nil.不為nil就直接返回view,等于nil就去調用loadView方法。loadView方法會去判斷有無指定storyboard/Xib文件,如果有就去加載storyboard/Xib描述的控制器View,如果沒有則系統默認創建一個空的view,賦給self.view。loadView方法有可能被調用多次(每當訪問self.view并且為nil時就會調用一次);
- viewDidLoad:view加載完成時調用(在loadView方法執行后調用)也有可能執行多次(self.view==nil且被訪問時)
-
awakeFromNib:通過storyboard創建控制器加載了控制器以及控制器view的nib文件,此方法在initWithCoder調用后被調用。而通過Xib創建控制器只加載了控制器view的nib文件,所以控制器的awakeFromNib方法不會被調用
awakeFromNib方法是在將統一歸檔中的所有對象都讀取并初始化完成后才會被調用。
4.viewWillAppearview即將可見時調用
5.viewWillLayoutSubViews:view即將布局子視圖時調用
6.viewDidLayoutSubviews:view完成子視圖布局后調用
7.viewDidAppear:view已經顯示后調用
8.viewWillDisappear:view即將消失、被覆蓋或者隱藏事調用此方法
9.viewDidDisappear:view已經消失、被覆蓋或者隱藏時調用此方法
10.didReceiveMemoryWarning:當收到內存警告時調用此方法
11.viewWillUnload:當內存過低時,需要釋放一些不需要使用的視圖時,即將釋放時調用(iOS6以后被廢棄)
12.viewDidUnload:當內存過低,釋放了一些不需要的視圖時調用(iOS6以后被廢棄)
總結如下圖所示
view的聲明周期圖
內存警告時view的處理機制
- iOS6以前(不包含iOS6)當內存警告時,我們會在
viewDidUnload
中手動回收viewController
的子視圖或者ViewController
的view([self.view removeFromSuperVIew];self.view = nil;)
,當view
再次被訪問到時,就會調用loadView
方法,viewController
的view
及其子視圖會被重新創建。 - 內存警告時,viewDidUnload一定會被調用
- loadView會被調用多次
iOS6以后蘋果廢棄了viewWillUnload
和viewDidUnload
方法,所以以前在viewDidUnload
中處理內存警告的代碼就需要移動到didReceiveMemoryWarning
中。但如果你覺得這樣就沒有問題了,那就錯了。iOS6以后蘋果廢棄了那兩個方法的同時也添加了內部處理view的方式,具體的處理機制如下:
iOS6及以后,內存警告時系統會回收ViewController的View的CALayer里的BitMap(CABackingStore類型,它的內容是直接用于渲染到屏幕,它是View消耗內存的大戶)。view和calayer占的內存極少, 數量級也就在byte和kbyte之間,所以系統只回收了BitMap,但是這里所謂的回收只是給BitMap占用的內存打了一個volatile標記表明這部分內存是可能隨時被其它數據占用,平時沒內存警告時正在使用的內存標記為In use,完全被釋放回收的標記為Not in use。概括起來也就是說:iOS6及以后的內存警告時,系統會給用于渲染視圖的數據(BitMap)內存打一個volatile, ViewController的View的架子結構并不會回收,當View再次被訪問時,雖然View的架子結構會用重建,但觸發drawRect來渲染界面時,如果view對應的BitMap數據內存沒有被占用則會被View的drawRect方法直接渲染出來且內存被標記為in use,從而這塊內存又可以獨享了;如果已被其它數據占用,那么BitMap必須要重建。所以可以看到整個重建過程不再是由loadView來做的,它是通過對view的訪問來觸發的。但是,請注意, 如果說在iOS6及以后ViewController的loadView方法只會被調用一次,這種說法是不完全準確的。因為:如果在didReceiveMemoryWarning里把ViewController的View也回收了([self.view removeFromSuperview];self.view = nil;),那么當再次有對View訪問時,loadView會被調用以進行完全最徹底的重建(想想也是,ViewController的View都沒了,不調loadView來重建那怎么辦呢)。
iOS6這種的設計的優點:
- 視圖結構和視圖數據的分離;
- 內存警告后系統只回收的是內存大戶視圖數據,但是回收不是完全的清掉,而只是做個標記,這樣既做到減小了每次重建BitMap 的成本,同時也把這部分內存開放出去可以隨時被別的數據占用;
- 重建時,充其量是重建BitMap(沒被占用時是直接用不用重建)