iOS程序執(zhí)行順序和UIViewController 的生命周期

一. iOS程序的啟動(dòng)執(zhí)行順序

程序啟動(dòng)順序圖

2252551-4c489571cabf79b7.png

具體執(zhí)行流程

1.程序入口
進(jìn)入main函數(shù),設(shè)置AppDelegate成為函數(shù)的代理

2程序完成加載

[AppDelegate application:didFinishLaunchingWithOptions:]

3.創(chuàng)建window窗口

4.程序被激活

[AppDelegate applicationDidBecomeActive:]

5.當(dāng)點(diǎn)擊command+H時(shí)(針對(duì)模擬器,手機(jī)是當(dāng)點(diǎn)擊home鍵)
程序取消激活狀態(tài)

[AppDelegate applicationWillResignActive:];

程序進(jìn)入后臺(tái)

[AppDelegate applicationDidEnterBackground:];

6.點(diǎn)擊進(jìn)入工程
程序進(jìn)入前臺(tái)

[AppDelegate applicationWillEnterForeground:]

程序被激活

[AppDelegate applicationDidBecomeActive:];

分析

  1. 對(duì)于applicationWillResignActive(非活動(dòng))與applicationDidEnterBackground(后臺(tái))這兩個(gè)的區(qū)別。
  • applicationWillResignActive(非活動(dòng)):
    比如當(dāng)有電話進(jìn)來(lái)或短信進(jìn)來(lái)或鎖屏等情況下,這時(shí)應(yīng)用程序掛起進(jìn)入非活動(dòng)狀態(tài),也就是手機(jī)界面還是顯示著你當(dāng)前的應(yīng)用程序的窗口,只不過(guò)被別的任務(wù)強(qiáng)制占用了,也可能是即將進(jìn)入后臺(tái)狀態(tài)(因?yàn)橐冗M(jìn)入非活動(dòng)狀態(tài)然后進(jìn)入后臺(tái)狀態(tài))

  • applicationDidEnterBackground(后臺(tái)):
    指當(dāng)前窗口不是你的App,大多數(shù)程序進(jìn)入這個(gè)后臺(tái)會(huì)在這個(gè)狀態(tài)上停留一會(huì),時(shí)間到之后會(huì)進(jìn)入掛起狀態(tài)(Suspended)。如果你程序特殊處理后可以長(zhǎng)期處于后臺(tái)狀態(tài)也可以運(yùn)行。
    Suspended (掛起): 程序在后臺(tái)不能執(zhí)行代碼。系統(tǒng)會(huì)自動(dòng)把程序變成這個(gè)狀態(tài)而且不會(huì)發(fā)出通知。當(dāng)掛起時(shí),程序還是停留在內(nèi)存中的,當(dāng)系統(tǒng)內(nèi)存低時(shí),系統(tǒng)就把掛起的程序清除掉,為前臺(tái)程序提供更多的內(nèi)存。

如下圖所示:

2252551-d87fdaa6730ba561.png

2.UIApplicationMain 函數(shù)解釋:

入口函數(shù):

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 參數(shù)是為了與C語(yǔ)言保持一致。

  • principalClassName (主要類名) 和 delegateClassName (委托類名)。
    (1) 如果principalClassName是nil,那么它的值將從Info.plist去獲取,如果Info.plist沒有,則默認(rèn)為UIApplication。principalClass這個(gè)類除了管理整個(gè)程序的生命周期之外什么都不做,它只負(fù)責(zé)監(jiān)聽事件然后交給delegateClass去做。
    (2) delegateClass 將在工程新建時(shí)實(shí)例化一個(gè)對(duì)象。NSStringFromClass([AppDelegate class])

  • AppDelegate 類實(shí)現(xiàn)文件

- (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__);
}

打印調(diào)用順序
啟動(dòng)程序

 --- -[AppDelegate application:didFinishLaunchingWithOptions:] ---
 --- -[AppDelegate applicationDidBecomeActive:] ---

按下 Command + H + SHIFT

--- -[AppDelegate applicationWillResignActive:] ---
--- -[AppDelegate applicationDidEnterBackground:] ---

重新點(diǎn)擊 進(jìn)入程序

--- -[AppDelegate applicationWillEnterForeground:] ---
--- -[AppDelegate applicationDidBecomeActive:] ---

選擇 模擬器的Simulate Memory Warning

--- -[AppDelegate applicationDidReceiveMemoryWarning:] ---

分析:
1.application:didFinishLaunchingWithOptions:
程序首次已經(jīng)完成啟動(dòng)時(shí)執(zhí)行,一般在這個(gè)函數(shù)里創(chuàng)建window對(duì)象,將程序內(nèi)容通過(guò)window呈現(xiàn)給用戶。
2.applicationWillResignActive(非活動(dòng))
程序?qū)⒁ctive狀態(tài)時(shí)調(diào)用,比如有電話進(jìn)來(lái)或者按下Home鍵,之后程序進(jìn)入后臺(tái)狀態(tài),對(duì)應(yīng)的applicationWillEnterForeground(即將進(jìn)入前臺(tái))方法。

該函數(shù)里面主要執(zhí)行操作:
a . 暫停正在執(zhí)行的任務(wù)
b. 禁止計(jì)時(shí)器
c. 減少OpenGL ES幀率
d. 若為游戲應(yīng)暫停游戲

3.applicationDidEnterBackground(已經(jīng)進(jìn)入后臺(tái))
對(duì)應(yīng)applicationDidBecomeActive(已經(jīng)變成前臺(tái))

該方法用來(lái):
a. 釋放共享資源
b. 保存用戶數(shù)據(jù)(寫到硬盤)
c. 作廢計(jì)時(shí)器
d. 保存足夠的程序狀態(tài)以便下次修復(fù);

4.applicationWillEnterForeground(即將進(jìn)入前臺(tái))
程序即將進(jìn)入前臺(tái)時(shí)調(diào)用,對(duì)應(yīng)applicationWillResignActive(即將進(jìn)入后臺(tái)),
這個(gè)方法用來(lái): 撤銷applicationWillResignActive中做的改變。

5.applicationDidBecomeActive(已經(jīng)進(jìn)入前臺(tái))
程序已經(jīng)變?yōu)锳ctive(前臺(tái))時(shí)調(diào)用。對(duì)應(yīng)applicationDidEnterBackground(已經(jīng)進(jìn)入后臺(tái))。
注意: 若程序之前在后臺(tái),在此方法內(nèi)刷新用戶界面

6.applicationWillTerminate
程序即將退出時(shí)調(diào)用。記得保存數(shù)據(jù),如applicationDidEnterBackground方法一樣。

二. UIViewController 的 生命周期

代碼 示例

#pragma mark --- life circle

// 非storyBoard(xib或非xib)都走這個(gè)方法
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

    }
    return self;
}

// 如果連接了串聯(lián)圖storyBoard 走這個(gè)方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
     NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithCoder:aDecoder]) {

    }
    return self;
}

// xib 加載 完成
- (void)awakeFromNib {
    [super awakeFromNib];
     NSLog(@"%s", __FUNCTION__);
}

// 加載視圖(默認(rèn)從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];
}


//視圖將要出現(xiàn)
- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillAppear:animated];
}

// view 即將布局其 Subviews
- (void)viewWillLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillLayoutSubviews];
}

// view 已經(jīng)布局其 Subviews
- (void)viewDidLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidLayoutSubviews];
}

//視圖已經(jīng)出現(xiàn)
- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidAppear:animated];
}

//視圖將要消失
- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillDisappear:animated];
}

//視圖已經(jīng)消失
- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidDisappear:animated];
}

//出現(xiàn)內(nèi)存警告  //模擬內(nèi)存警告:點(diǎn)擊模擬器->hardware-> Simulate Memory Warning
- (void)didReceiveMemoryWarning {
    NSLog(@"%s", __FUNCTION__);
    [super didReceiveMemoryWarning];
}

// 視圖被銷毀
- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}

查看 打印 結(jié)果

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,執(zhí)行關(guān)鍵數(shù)據(jù)初始化操作,非StoryBoard創(chuàng)建UIViewController都會(huì)調(diào)用這個(gè)方法。
注意: 不要在這里做View相關(guān)操作,View在loadView方法中才初始化。

  1. initWithCoder:
    如果使用StoryBoard進(jìn)行視圖管理,程序不會(huì)直接初始化一個(gè)UIViewController,StoryBoard會(huì)自動(dòng)初始化或在segue被觸發(fā)時(shí)自動(dòng)初始化,因此方法initWithNibName:bundle不會(huì)被調(diào)用,但是initWithCoder會(huì)被調(diào)用。

  2. awakeFromNib
    當(dāng)awakeFromNib方法被調(diào)用時(shí),所有視圖的outlet和action已經(jīng)連接,但還沒有被確定,這個(gè)方法可以算作適合視圖控制器的實(shí)例化配合一起使用的,因?yàn)橛行┬枰鶕?jù)用戶喜好來(lái)進(jìn)行設(shè)置的內(nèi)容,無(wú)法存在storyBoard或xib中,所以可以在awakeFromNib方法中被加載進(jìn)來(lái)。

  3. loadView
    當(dāng)執(zhí)行到loadView方法時(shí),如果視圖控制器是通過(guò)nib創(chuàng)建,那么視圖控制器已經(jīng)從nib文件中被解檔并創(chuàng)建好了,接下來(lái)任務(wù)就是對(duì)view進(jìn)行初始化。

loadView方法在UIViewController對(duì)象的view被訪問(wèn)且為空的時(shí)候調(diào)用。這是它與awakeFromNib方法的一個(gè)區(qū)別。
假設(shè)我們?cè)谔幚韮?nèi)存警告時(shí)釋放view屬性:self.view = nil。因此loadView方法在視圖控制器的生命周期內(nèi)可能被調(diào)用多次。
loadView方法不應(yīng)該直接被調(diào)用,而是由系統(tǒng)調(diào)用。它會(huì)加載或創(chuàng)建一個(gè)view并把它賦值給UIViewController的view屬性。

在創(chuàng)建view的過(guò)程中,首先會(huì)根據(jù)nibName去找對(duì)應(yīng)的nib文件然后加載。如果nibName為空或找不到對(duì)應(yīng)的nib文件,則會(huì)創(chuàng)建一個(gè)空視圖(這種情況一般是純代碼)

注意:在重寫loadView方法的時(shí)候,不要調(diào)用父類的方法。

  1. viewDidLoad
    當(dāng)loadView將view載入內(nèi)存中,會(huì)進(jìn)一步調(diào)用viewDidLoad方法來(lái)進(jìn)行進(jìn)一步設(shè)置。此時(shí),視圖層次已經(jīng)放到內(nèi)存中,通常,我們對(duì)于各種初始化數(shù)據(jù)的載入,初始設(shè)定、修改約束、移除視圖等很多操作都可以這個(gè)方法中實(shí)現(xiàn)。

視圖層次(view hierachy):因?yàn)槊總€(gè)視圖都有自己的子視圖,這個(gè)視圖層次其實(shí)也可以理解為一顆樹狀的數(shù)據(jù)結(jié)構(gòu)。而樹的根節(jié)點(diǎn),也就是根視圖(root view),在UIViewController中以view屬性。它可以看做是其他所有子視圖的容器,也就是根節(jié)點(diǎn)。

  1. viewWillAppear
    系統(tǒng)在載入所有的數(shù)據(jù)后,將會(huì)在屏幕上顯示視圖,這時(shí)會(huì)先調(diào)用這個(gè)方法,通常我們會(huì)在這個(gè)方法對(duì)即將顯示的視圖做進(jìn)一步的設(shè)置。比如,設(shè)置設(shè)備不同方向時(shí)該如何顯示;設(shè)置狀態(tài)欄方向、設(shè)置視圖顯示樣式等。

另一方面,當(dāng)APP有多個(gè)視圖時(shí),上下級(jí)視圖切換是也會(huì)調(diào)用這個(gè)方法,如果在調(diào)入視圖時(shí),需要對(duì)數(shù)據(jù)做更新,就只能在這個(gè)方法內(nèi)實(shí)現(xiàn)。

  1. viewWillLayoutSubviews
    view 即將布局其Subviews。 比如view的bounds改變了(例如:狀態(tài)欄從不顯示到顯示,視圖方向變化),要調(diào)整Subviews的位置,在調(diào)整之前要做的工作可以放在該方法中實(shí)現(xiàn)

8.viewDidLayoutSubviews
view已經(jīng)布局其Subviews,這里可以放置調(diào)整完成之后需要做的工作。

  1. viewDidAppear
    在view被添加到視圖層級(jí)中以及多視圖,上下級(jí)視圖切換時(shí)調(diào)用這個(gè)方法,在這里可以對(duì)正在顯示的視圖做進(jìn)一步的設(shè)置。

10.viewWillDisappear
在視圖切換時(shí),當(dāng)前視圖在即將被移除、或被覆蓋是,會(huì)調(diào)用該方法,此時(shí)還沒有調(diào)用removeFromSuperview。

  1. viewDidDisappear
    view已經(jīng)消失或被覆蓋,此時(shí)已經(jīng)調(diào)用removeFromSuperView;

  2. dealloc
    視圖被銷毀,此次需要對(duì)你在init和viewDidLoad中創(chuàng)建的對(duì)象進(jìn)行釋放。

13.didReceiveMemoryWarning
在內(nèi)存足夠的情況下,app的視圖通常會(huì)一直保存在內(nèi)存中,但是如果內(nèi)存不夠,一些沒有正在顯示的viewController就會(huì)收到內(nèi)存不足的警告,然后就會(huì)釋放自己擁有的視圖,以達(dá)到釋放內(nèi)存的目的。但是系統(tǒng)只會(huì)釋放內(nèi)存,并不會(huì)釋放對(duì)象的所有權(quán),所以通常我們需要在這里將不需要顯示在內(nèi)存中保留的對(duì)象釋放它的所有權(quán),將其指針置nil。

三. 視圖的生命歷程

  • [ViewController initWithCoder:]或[ViewController initWithNibName:Bundle]: 首先從歸檔文件中加載UIViewController對(duì)象。即使是純代碼,也會(huì)把nil作為參數(shù)傳給后者。
  • -[ViewController viewDidLoad]: 此時(shí)整個(gè)視圖層次(view hierarchy)已經(jīng)放到內(nèi)存中,可以移除一些視圖,修改約束,加載數(shù)據(jù)等。
  • [ViewController viewWillLayoutSubviews]:即將開始子視圖位置布局
  • [ViewController viewDidLayoutSubviews]:用于通知視圖的位置布局已經(jīng)完成
  • [ViewController viewDidAppear:]:視圖已經(jīng)展示在屏幕上,可以對(duì)視圖做一些關(guān)于展示效果方面的修改。
  • [ViewController viewWillDisappear:]:視圖即將消失
  • [ViewController viewDidDisappear:]:視圖已經(jīng)消失

四: 總結(jié):

  • 只有init系列的方法,如initWithNibName需要自己調(diào)用,其他方法如loadView和awakeFromNib則是系統(tǒng)自動(dòng)調(diào)用。而viewWill/Did系列的方法則類似于回調(diào)和通知,也會(huì)被自動(dòng)調(diào)用。

  • 純代碼寫視圖布局時(shí)需要注意,要手動(dòng)調(diào)用loadView方法,而且不要調(diào)用父類的loadView方法。純代碼和用IB的區(qū)別僅存在于loadView方法及其之前,編程時(shí)需要注意的也就是loadView方法。

  • 除了initWithNibName和awakeFromNib方法是處理視圖控制器外,其他方法都是處理視圖。這兩個(gè)方法在視圖控制器的生命周期里只會(huì)調(diào)用一次。

作者:林大鵬天地
鏈接:http://www.itdecent.cn/p/d60b388b19f5
來(lái)源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容