標籤:life win com ted rip 3.3 jpeg blog 控制器
說明:此文是自己的總結筆記,主要參考:iOS程式的啟動執行順序 AppDelegate 及 UIViewController 的生命週期UIView的生命週期
言葉之庭.jpeg一. iOS程式的啟動執行順序
程式啟動順序圖
iOS啟動原理圖.png
具體執行流程
程式入口
進入main
函數,設定AppDelegate
稱為函數的代理
程式完成載入
[AppDelegate application:didFinishLaunchingWithOptions:]
建立window
視窗
程式被啟用
[AppDelegate applicationDidBecomeActive:]
當點擊command+H
時(針對模擬器,手機是當點擊home鍵
)
程式取消啟用狀態
[AppDelegate applicationWillResignActive:];
程式進入後台
[AppDelegate applicationDidEnterBackground:];
點擊進入工程
程式進入前台
[AppDelegate applicationWillEnterForeground:]
程式被啟用
[AppDelegate applicationDidBecomeActive:];
分析
1. 對於applicationWillResignActive(非活動)
與applicationDidEnterBackground(後台)
這兩個的區別。
applicationWillResignActive(非活動)
:
比如當有電話進來或簡訊進來或鎖屏等情況下,這時應用程式掛起進入非使用中,也就是手機介面還是顯示著你當前的應用程式的視窗,只不過被別的任務強制佔用了,也可能是即將進入後台狀態(因為要先進入非使用中然後進入後台狀態)
applicationDidEnterBackground(後台)
:
指當前視窗不是你的App,大多數程式進入這個後台會在這個狀態上停留一會,時間到之後會進入掛起狀態(Suspended)
。如果你程式特殊處理後可以長期處於後台狀態也可以運行。
Suspended (掛起)
: 程式在後台不能執行代碼。系統會自動把程式變成這個狀態而且不會發出通知。當掛起時,程式還是停留在記憶體中的,當系統記憶體低時,系統就把掛起的程式清除掉,為前景程式提供更多的記憶體。
如所示:
活動和非活動.png
2.UIApplicationMain
函數解釋:
入口函數:
int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
argc
和 argv
參數是為了與C語言保持一致。
principalClassName (主要類名)
和 delegateClassName (委託類名)
。
(1) 如果principalClassName
是nil,那麼它的值將從Info.plist
去擷取,如果Info.plist
沒有,則預設為UIApplication
。principalClass
這個類除了管理整個程式的生命週期之外什麼都不做,它只負責監聽事件然後交給delegateClass
去做。
(2) delegateClass
將在工程建立時執行個體化一個對象。NSStringFromClass([AppDelegate class])
AppDelegate
類實現檔案
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"--- %s ---",__func__);//__func__列印方法名 return YES;}- (void)applicationWillResignActive:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}- (void)applicationDidEnterBackground:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}- (void)applicationWillEnterForeground:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}- (void)applicationDidBecomeActive:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}- (void)applicationWillTerminate:(UIApplication *)application { NSLog(@"--- %s ---",__func__);}
列印調用順序
啟動程式
--- -[AppDelegate application:didFinishLaunchingWithOptions:] --- --- -[AppDelegate applicationDidBecomeActive:] ---
按下 Command + H + SHIFT
--- -[AppDelegate applicationWillResignActive:] ------ -[AppDelegate applicationDidEnterBackground:] ---
重新點擊 進入程式
--- -[AppDelegate applicationWillEnterForeground:] ------ -[AppDelegate applicationDidBecomeActive:] ---
選擇 模擬器的Simulate Memory Warning
--- -[AppDelegate applicationDidReceiveMemoryWarning:] ---
分析:
1.application:didFinishLaunchingWithOptions
:
程式首次已經完成啟動時執行,一般在這個函數裡建立window對象,將程式內容通過window呈現給使用者。
applicationWillResignActive(非活動)
程式將要失去Active
狀態時調用,比如有電話
進來或者按下Home鍵
,之後程式進入後台狀態,對應的applicationWillEnterForeground(即將進入前台)
方法。
該函數裡面主要執行操作:
a . 暫停正在執行的任務
b. 禁止計時器
c. 減少OpenGL ES
幀率
d. 若為遊戲應暫停遊戲
3.applicationDidEnterBackground(已經進入後台)
對應applicationDidBecomeActive(已經變成前台)
該方法用來:
a. 釋放共用資源
b. 儲存使用者資料(寫到硬碟)
c. 作廢計時器
d. 儲存足夠的程式狀態以便下次修複;
applicationWillEnterForeground(即將進入前台)
程式即將進入前台時調用,對應applicationWillResignActive(即將進入後台)
,
這個方法用來: 撤銷applicationWillResignActive
中做的改變。
applicationDidBecomeActive(已經進入前台)
程式已經變為Active(前台)
時調用。對應applicationDidEnterBackground(已經進入後台)
。
注意: 若程式之前在後台,在此方法內重新整理使用者介面
applicationWillTerminate
程式即將退出時調用。記得儲存資料,如applicationDidEnterBackground
方法一樣。
等候.jpeg二.
UIViewController
的 生命週期
代碼 樣本
#pragma mark --- life circle// 非storyBoard(xib或非xib)都走這個方法- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { NSLog(@"%s", __FUNCTION__); if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { } return self;}// 如果串連了串聯圖storyBoard 走這個方法- (instancetype)initWithCoder:(NSCoder *)aDecoder { NSLog(@"%s", __FUNCTION__); if (self = [super initWithCoder:aDecoder]) { } return self;}// xib 載入 完成- (void)awakeFromNib { [super awakeFromNib]; NSLog(@"%s", __FUNCTION__);}// 載入視圖(預設從nib)- (void)loadView { NSLog(@"%s", __FUNCTION__); self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.view.backgroundColor = [UIColor redColor];}//視圖控制器中的視圖載入完成,viewController內建的view載入完成- (void)viewDidLoad { NSLog(@"%s", __FUNCTION__); [super viewDidLoad];}//視圖將要出現- (void)viewWillAppear:(BOOL)animated { NSLog(@"%s", __FUNCTION__); [super viewWillAppear:animated];}// view 即將布局其 Subviews- (void)viewWillLayoutSubviews { NSLog(@"%s", __FUNCTION__); [super viewWillLayoutSubviews];}// view 已經布局其 Subviews- (void)viewDidLayoutSubviews { NSLog(@"%s", __FUNCTION__); [super viewDidLayoutSubviews];}//視圖已經出現- (void)viewDidAppear:(BOOL)animated { NSLog(@"%s", __FUNCTION__); [super viewDidAppear:animated];}//視圖將要消失- (void)viewWillDisappear:(BOOL)animated { NSLog(@"%s", __FUNCTION__); [super viewWillDisappear:animated];}//視圖已經消失- (void)viewDidDisappear:(BOOL)animated { NSLog(@"%s", __FUNCTION__); [super viewDidDisappear:animated];}//出現記憶體警告 //類比記憶體警告:點擊模擬器->hardware-> Simulate Memory Warning- (void)didReceiveMemoryWarning { NSLog(@"%s", __FUNCTION__); [super didReceiveMemoryWarning];}// 視圖被銷毀- (void)dealloc { NSLog(@"%s", __FUNCTION__);}
查看 列印 結果
2017-03-01 18:03:41.577 ViewControllerLifeCircle[32254:401790] -[ViewController initWithCoder:]2017-03-01 18:03:41.579 ViewControllerLifeCircle[32254:401790] -[ViewController awakeFromNib]2017-03-01 18:03:41.581 ViewControllerLifeCircle[32254:401790] -[ViewController loadView]2017-03-01 18:03:46.485 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLoad]2017-03-01 18:03:46.486 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillAppear:]2017-03-01 18:03:46.487 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillLayoutSubviews]2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLayoutSubviews]2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillLayoutSubviews]2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLayoutSubviews]2017-03-01 18:03:46.490 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidAppear:]2017-03-01 19:03:13.308 ViewControllerLifeCircle[32611:427962] -[ViewController viewWillDisappear:]2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController viewDidDisappear:]2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController dealloc]2017-03-01 19:12:05.927 ViewControllerLifeCircle[32611:427962] -[ViewController didReceiveMemoryWarning]
分析
1.initWithNibName:bundle
:
初始化UIViewController
,執行關鍵資料初始化操作,非StoryBoard
建立UIViewController
都會調用這個方法。
注意: 不要在這裡做View
相關操作,View
在loadView
方法中才初始化。
2. initWithCoder:
如果使用StoryBoard
進行視圖管理,程式不會直接初始化一個UIViewController
,StoryBoard
會自動初始化或在segue
被觸發時自動初始化,因此方法initWithNibName:bundle
不會被調用,但是initWithCoder
會被調用。
3. awakeFromNib
當awakeFromNib
方法被調用時,所有視圖的outlet
和action
已經串連,但還沒有被確定,這個方法可以算作適合視圖控制器的執行個體化配合一起使用的,因為有些需要根據使用者洗好來進行設定的內容,無法存在storyBoard
或xib
中,所以可以在awakeFromNib
方法中被載入進來。
4. loadView
當執行到loadView
方法時,如果視圖控制器是通過nib
建立,那麼視圖控制器已經從nib
檔案中被解檔並建立好了,接下來任務就是對view
進行初始化。
loadView
方法在UIViewController
對象的view
被訪問且為空白的時候調用。這是它與awakeFromNib
方法的一個區別。
假設我們在處理記憶體警告時釋放view
屬性:self.view = nil
。因此loadView
方法在視圖控制器的生命週期內可能被調用多次。
loadView
方法不應該直接被調用,而是由系統調用。它會載入或建立一個view並把它賦值給UIViewController
的view
屬性。
在建立view
的過程中,首先會根據nibName
去找對應的nib
檔案然後載入。如果nibName
為空白或找不到對應的nib
檔案,則會建立一個空視圖(這種情況一般是純程式碼)
注意:在重寫loadView方法的時候,不要調用父類的方法。
5. viewDidLoad
當loadView
將view
載入記憶體中,會進一步調用viewDidLoad
方法來進行進一步設定。此時,視圖層次已經放到記憶體中,通常,我們對於各種初始化資料的載入,初始設定、修改約束、移除視圖等很多操作都可以這個方法中實現。
視圖層次(view hierachy):因為每個視圖都有自己的子視圖,這個視圖層次其實也可以理解為一顆樹狀的資料結構。而樹的根節點,也就是根視圖(root view)
,在UIViewController
中以view
屬性。它可以看做是其他所有子視圖的容器,也就是根節點。
6. viewWillAppear
系統在載入所有的資料後,將會在螢幕上顯示視圖,這時會先調用這個方法,通常我們會在這個方法對即將顯示的視圖做進一步的設定。比如,設定裝置不同方向時該如何顯示;設定狀態列方向、設定視圖顯示樣式等。
另一方面,當APP
有多個視圖時,上下級視圖切換是也會調用這個方法,如果在調入視圖時,需要對資料做更新,就只能在這個方法內實現。
7. viewWillLayoutSubviews
view
即將布局其Subviews
。 比如view
的bounds
改變了(例如:狀態列從不顯示到顯示,視圖方向變化),要調整Subviews
的位置,在調整之前要做的工作可以放在該方法中實現
8.viewDidLayoutSubviews
view
已經布局其Subviews
,這裡可以放置調整完成之後需要做的工作。
9. viewDidAppear
在view被添加到視圖層級中以及多視圖,上下級視圖切換時調用這個方法,在這裡可以對正在顯示的視圖做進一步的設定。
10.viewWillDisappear
在視圖切換時,當前視圖在即將被移除、或被覆蓋是,會調用該方法,此時還沒有調用removeFromSuperview
。
11. viewDidDisappear
view
已經消失或被覆蓋,此時已經調用removeFromSuperView
;
12. dealloc
視圖被銷毀,此次需要對你在init
和viewDidLoad
中建立的對象進行釋放。
13.didReceiveMemoryWarning
在記憶體足夠的情況下,app```的視圖通常會一直儲存在記憶體中,但是如果記憶體不夠,一些沒有正在顯示的
viewController就會收到記憶體不足的警告,然後就會釋放自己擁有的視圖,以達到釋放記憶體的目的。但是系統只會釋放記憶體,並不會釋放對象的所有權,所以通常我們需要在這裡將不需要顯示在記憶體中保留的對象釋放它的所有權,將其指標置
nil``。
三. 視圖的生命曆程
[ViewController initWithCoder:]
或[ViewController initWithNibName:Bundle]
: 首先從歸檔檔案中載入UIViewController
對象。即使是純程式碼,也會把nil
作為參數傳給後者。
[UIView awakeFromNib]:
作為第一個方法的助手,方法處理一些額外的設定。
[ViewController loadView]
: 建立或載入一個view
並把它賦值給UIViewController
的view
屬性。
-[ViewController viewDidLoad]
: 此時整個視圖層次(view hierarchy)
已經放到記憶體中,可以移除一些視圖,修改約束,載入資料等。
[ViewController viewWillAppear:]
: 視圖載入完成,並即將顯示在螢幕上。還沒設定動畫,可以改變當前螢幕方向或狀態列的風格等。
[ViewController viewWillLayoutSubviews]
:即將開始子視圖位置布局
[ViewController viewDidLayoutSubviews]
:用於通知視圖的位置布局已經完成
[ViewController viewDidAppear:]
:視圖已經展示在螢幕上,可以對視圖做一些關於展示效果方面的修改。
[ViewController viewWillDisappear:]
:視圖即將消失
[ViewController viewDidDisappear:]
:視圖已經消失四: 總結:
只有init
系列的方法,如initWithNibName
需要自己調用,其他方法如loadView
和awakeFromNib
則是系統自動調用。而viewWill/Did
系列的方法則類似於回調和通知,也會被自動調用。
純程式碼寫視圖布局時需要注意,要手動調用loadView
方法,而且不要調用父類的loadView
方法。純程式碼和用IB
的區別僅存在於loadView
方法及其之前,編程時需要注意的也就是loadView
方法。
除了initWithNibName
和awakeFromNib
方法是處理視圖控制器外,其他方法都是處理視圖。這兩個方法在視圖控制器的生命週期裡只會調用一次。
轉自:http://www.jianshu.com/p/d60b388b19f5
iOS程式執行順序和UIViewController 的生命週期(整理)