iOS 生命周期

APP 生命周期

當(dāng)我們打開 APP 時(shí)徒坡,程序一般都是從 main 函數(shù)開始運(yùn)行的,那么我們先來(lái)看下 Xcode 自動(dòng)生成的 main.m 文件:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

這個(gè)默認(rèn)的 iOS 程序就是從 main 函數(shù)開始執(zhí)行的腐宋,但是在 main 函數(shù)中我們其實(shí)只能看到一個(gè)方法,這個(gè)方法內(nèi)部是一個(gè)消息循環(huán)(相當(dāng)于一個(gè)死循環(huán))檀轨,因此運(yùn)行到這個(gè)方法 UIApplicationMain 之后程序不會(huì)自動(dòng)退出脏款,而只有當(dāng)用戶手動(dòng)關(guān)閉程序這個(gè)循環(huán)才結(jié)束。我們看下這個(gè)方法定義:

int UIApplicationMain(int argc, char * _Nullable *argv, NSString *principalClassName, NSString *delegateClassName);

這個(gè)方法有四個(gè)參數(shù):

  • argc:參數(shù)個(gè)數(shù)裤园,與 main 函數(shù)的參數(shù)對(duì)應(yīng)。
  • argv:參數(shù)內(nèi)容剂府,與 main 函數(shù)的參數(shù)對(duì)應(yīng)拧揽。
  • principalClassName:代表 UIApplication 類或其子類。這個(gè)參數(shù)默認(rèn)為 nil腺占,則代表 UIApplication 類淤袜。UIApplication 是單例模式,一個(gè)應(yīng)用程序只有一個(gè) UIApplication 對(duì)象或子對(duì)象衰伯。
  • delegateClassName:代理铡羡,默認(rèn)生成的是 AppDelegate 類,這個(gè)類主要用于監(jiān)聽整個(gè)應(yīng)用程序生命周期的各個(gè)事件意鲸,當(dāng)UIApplication運(yùn)行過程中引發(fā)了某個(gè)事件之后會(huì)調(diào)用代理中對(duì)應(yīng)的方法烦周。

關(guān)于返回值尽爆,即便聲明了返回值,但該函數(shù)也從不會(huì)返回读慎。

也就是說(shuō)當(dāng)執(zhí)行 UIApplicationMain 方法后這個(gè)方法會(huì)根據(jù)第三個(gè)參數(shù)principalClassName創(chuàng)建對(duì)應(yīng)的 UIApplication 對(duì)象漱贱,這個(gè)對(duì)象會(huì)根據(jù)第四個(gè)參數(shù)delegateClassName 創(chuàng)建 AppDelegate 并指定此對(duì)象為 UIApplication 的代理;同時(shí) UIApplication 會(huì)開啟一個(gè)消息循環(huán)不斷監(jiān)聽?wèi)?yīng)用程序的各個(gè)活動(dòng)夭委,當(dāng)應(yīng)用程序生命周期發(fā)生改變 UIApplication 就會(huì)調(diào)用代理對(duì)應(yīng)的方法幅狮。

既然應(yīng)用程序 UIApplication 是通過代理和外部交互的,那么我們就有必要清楚 AppDelegate 的操作細(xì)節(jié)株灸,在這個(gè)類中定義了生命周期的各個(gè)事件的執(zhí)行方法:

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"程序已經(jīng)啟動(dòng)");
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    NSLog(@"程序?qū)⒁ソ裹c(diǎn)");
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"程序已經(jīng)進(jìn)入后臺(tái)");
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"程序?qū)⒁M(jìn)入前臺(tái)");
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSLog(@"程序獲得焦點(diǎn)");
}

- (void)applicationWillTerminate:(UIApplication *)application {
    NSLog(@"程序?qū)⒁K止");
}

@end

簡(jiǎn)要說(shuō)下我們不同的操作崇摄,程序運(yùn)行結(jié)果:

  • 啟動(dòng)程序

    程序已經(jīng)啟動(dòng)
    程序獲得焦點(diǎn)
    
  • 按下 home 鍵

    程序?qū)⒁ソ裹c(diǎn)
    程序已經(jīng)進(jìn)入后臺(tái)
    
  • 再次打開程序

    程序?qū)⒁M(jìn)入前臺(tái)
    程序獲得焦點(diǎn)
    
  • 下拉狀態(tài)欄

    程序?qū)⒁ソ裹c(diǎn)
    程序獲得焦點(diǎn)
    程序?qū)⒁ソ裹c(diǎn)
    
  • 狀態(tài)欄收回

    程序獲得焦點(diǎn)
    
  • 上拉控制中心

    程序?qū)⒁ソ裹c(diǎn)
    
  • 收回控制中心

    程序獲得焦點(diǎn)
    
  • 來(lái)電

    程序?qū)⒁ソ裹c(diǎn)
    
  • 斷電

    程序獲得焦點(diǎn)
    
  • 雙擊 Home 并關(guān)閉應(yīng)用

    程序?qū)⒁ソ裹c(diǎn)
    程序已經(jīng)進(jìn)入后臺(tái)
    程序?qū)⒁K止
    

通過簡(jiǎn)單的操作,大家對(duì)整個(gè)運(yùn)行周期有了個(gè)大概的了解慌烧。再附上一張圖逐抑,讓大家有個(gè)清晰的認(rèn)識(shí):

image

UIViewController 生命周期

總覽 UIViewController 生命周期:

image

下面創(chuàng)建了一個(gè) TestViewController 類,了解下整個(gè)過程:

TestViewController.m:

#import "TestViewController.h"

@interface TestViewController ()

@end

@implementation TestViewController

-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
   self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    NSLog(@"%s",__func__);
    return self;
}

-(instancetype)init{
    self = [super init];
    NSLog(@"%s",__func__);
    return self;
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    NSLog(@"%s",__func__);
    return self;
}

-(void)awakeFromNib{
    [super awakeFromNib];
    NSLog(@"%s",__func__);
}

-(void)loadView{
    [super loadView];
    NSLog(@"%s",__func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%s",__func__);
}

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    NSLog(@"%s",__func__);
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    NSLog(@"%s",__func__);
}

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    NSLog(@"%s",__func__);
}

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    NSLog(@"%s",__func__);
}

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    NSLog(@"%s",__func__);
}

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    NSLog(@"%s",__func__);
}

-(void)dealloc{
    NSLog(@"%s",__func__);
}

@end

UIViewController 初始化

在 ViewController.m 中:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    TestViewController *vc = [[TestViewController alloc]init];
    [self.navigationController pushViewController:vc animated:YES];
}

我們?cè)趧?chuàng)建 TestViewController 實(shí)例時(shí)杏死,可以通過以下兩種方法:

//第一種
[[TestViewController alloc]initWithNibName:@"ViewController" bundle:nil];

//第二種    
[[TestViewController alloc]init];

我們經(jīng)常使用的是第二種創(chuàng)建方法泵肄,其實(shí)第二種方法默認(rèn)實(shí)現(xiàn)了第一種的方法,只不過兩個(gè)參數(shù)默認(rèn)傳的是 nil淑翼。

當(dāng) TestVeiwController 通過 xib 加載的時(shí)候腐巢,看下 viewDidLoad 之前發(fā)生了什么:

-[TestViewController initWithNibName:bundle:]
-[TestViewController init]
-[TestViewController loadView]
-[TestViewController viewDidLoad]

無(wú) xib:

-[TestViewController initWithNibName:bundle:]
-[TestViewController init]
-[TestViewController loadView]
-[TestViewController viewDidLoad]

TestVeiwController 通過 storyboard 加載:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"TestViewController" bundle:nil];
    TestViewController *testVC = [storyboard instantiateInitialViewController];
    [self.navigationController pushViewController:testVC animated:YES];
}

控制臺(tái)輸出:

-[TestViewController initWithCoder:]
-[TestViewController awakeFromNib]
-[TestViewController loadView]
-[TestViewController viewDidLoad]

我們可以看到通過 storyboard 實(shí)例化與 init 實(shí)例化在 loadView方法調(diào)用之前走的是不同的方法。我們看下這幾個(gè)方法的不同:

initWithNibName:bundle:

此方法發(fā)生在 nib 加載之前玄括。

調(diào)用此方法進(jìn)行 Controller 初始化冯丙,與 nib 加載無(wú)關(guān)。nib 的加載是懶加載遭京,當(dāng) Controller 需要加載其視圖時(shí)胃惜,才會(huì)加載此方法中指定的 nib。

可以看出該方法初始化的 Controller 不是從 nib 創(chuàng)建的哪雕。

initWithCoder

此方法發(fā)生在 nib 加載期間船殉。

所有 archived 對(duì)象的初始化使用此方法。nib 中存儲(chǔ)的對(duì)象就是 archived 對(duì)象斯嚎,所以此方法是 nib 加載對(duì)象時(shí)使用的初始化方法利虫。

當(dāng)從 nib 創(chuàng)建 UIViewController 時(shí)使用此方法。

awakeFromNib

此方法發(fā)生在 nib 中所有對(duì)象都已完全加載完之后堡僻。

如果 initWithCoder是 unarchiving 開始糠惫,那此方法就是結(jié)束。

loadView 與 veiwDidLoad

在此方法中創(chuàng)建視圖钉疫。

我們可以通過下圖來(lái)理解它的邏輯:

image

每次訪問 view 時(shí)硼讽,就會(huì)調(diào)用 self.view 的 get 方法,在 get 方法中判斷self.view==nil牲阁,不為 nil 就直接返回 view固阁,等于 nil 就去調(diào)用 loadView 方法壤躲。loadView 方法會(huì)去判斷有無(wú)指定 storyBord/Xib 文件,如果有就去加載 storyBord/Xib 描述的控制器 view您炉,如果沒有則系統(tǒng)默認(rèn)創(chuàng)建一個(gè)空的 view柒爵,賦給 self.view。loadView 方法有可能被多次調(diào)用(每當(dāng)訪問 self.view 并且為 nil 時(shí)就會(huì)調(diào)用一次)赚爵;

系統(tǒng)會(huì)自動(dòng)為我們加載 view棉胀,我們完全沒必要手動(dòng)創(chuàng)建 view。

viewWillAppear

視圖將要被展示的時(shí)候調(diào)用冀膝。

其調(diào)用的時(shí)機(jī)與視圖所在層次有關(guān)唁奢。例如我們常用的 push 與 present 操作改變了當(dāng)前視圖層次,都會(huì)觸發(fā)此方法窝剖。

1麻掸、那么 UIAlertController 也是 present 操作怎么沒有觸發(fā)呢?

因?yàn)?UIAlertController 在另一個(gè) window 上赐纱,view 在自己所在的 window 中層次并沒有改變脊奋,所以不會(huì)觸發(fā),同理在鎖屏以及進(jìn)入后臺(tái)時(shí)也不會(huì)觸發(fā)疙描。

2诚隙、如果控制器 B 被展示在另一個(gè)控制器 A 的 popover 中,那么被展示的控制器 B 在消失后起胰,控制器 A 并不會(huì)調(diào)用此方法久又。

官方原文:

If a view controller is presented by a view controller inside of a popover, this method is not invoked on the presenting view controller after the presented controller is dismissed.

例如我們使用的addSubview方法,如下:

AViewController.m 中:

BViewController *B = [[BViewController alloc]init];
[self addChildViewController:B];
[self.view addSubview:B.view];

當(dāng)我們將 BViewController 從 AViewController 中移除后效五,并不會(huì)觸發(fā) AViewController 的 viewWillAppear 方法地消。

viewDidAppear

視圖渲染完成后調(diào)用,與viewWillAppear配套使用畏妖。

viewWillLayoutSubviews 與 viewDidLayoutSubviews

這兩個(gè)方法發(fā)生在 viewWillAppearviewDidAppear 之間脉执。

  • viewWillLayoutSubviews

    控制器將要布局 view 的子控件時(shí)調(diào)用,默認(rèn)實(shí)現(xiàn)為空戒劫。此時(shí)子控件的大小還沒有設(shè)置好半夷。

  • viewDidLayoutSubviews

    控制器已經(jīng)布局 view 的子控件時(shí)調(diào)用,默認(rèn)實(shí)現(xiàn)為空谱仪。此時(shí)子控件的大小才被設(shè)置好,這里才是獲取子視圖大小的正確位置否彩。

viewWillDisappear 與 viewDidDisappear

viewWillDisappearviewDidDisappear配套使用疯攒。

  • viewWillDisappear

    視圖將要消失時(shí)調(diào)用

  • viewDidDisappear

    視圖完全消失后調(diào)用

兩個(gè)方法的調(diào)用時(shí)機(jī)同viewWillAppearviewDidAppear道理相同。

didReceiveMemoryWarning 與 viewDidUnload

這兩個(gè)方法是收到內(nèi)存警告時(shí)調(diào)用的列荔。

  • viewDidUnload

在 iOS5 以及之前使用的方法敬尺,iOS6 及之后已經(jīng)廢棄枚尼。在收到內(nèi)存警告時(shí),在此方法中將 view 置為 nil;

  • didReceiveMemoryWarning

收到內(nèi)存警告時(shí)砂吞,系統(tǒng)自動(dòng)調(diào)用此方法署恍,回收占用大量?jī)?nèi)存的視圖數(shù)據(jù)。我們一般不需要在這里做額外的操作蜻直。如果要自己處理一些額外內(nèi)存盯质,重寫時(shí)需要調(diào)用父類方法,即[super didReceiveMemoryWarning]

dealloc

UIViewController 釋放時(shí)調(diào)用此方法。UIViewController 的生命周期到此結(jié)束劣挫。

當(dāng)我們重寫此方法時(shí)寻馏,ARC 環(huán)境下不需要調(diào)用父類方法,MRC 環(huán)境下需要調(diào)用父類方法泰佳,即[super dealloc]

小結(jié)

本篇主要介紹了 APP 的生命周期,以及 UIViewController 的生命周期压储,對(duì)我們程序開發(fā)的過程有了更清晰的認(rèn)識(shí)。

參考資料:

https://www.cnblogs.com/kenshincui/p/3890880.html

https://www.quora.com/Cocoa-API-What-is-the-difference-between-initWithCoder-initWithNibName-and-awakeFromNib-1

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末源譬,一起剝皮案震驚了整個(gè)濱河市集惋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瓶佳,老刑警劉巖芋膘,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異霸饲,居然都是意外死亡为朋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門厚脉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)习寸,“玉大人,你說(shuō)我怎么就攤上這事傻工∠枷” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵中捆,是天一觀的道長(zhǎng)鸯匹。 經(jīng)常有香客問我,道長(zhǎng)泄伪,這世上最難降的妖魔是什么殴蓬? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蟋滴,結(jié)果婚禮上染厅,老公的妹妹穿的比我還像新娘痘绎。我一直安慰自己,他們只是感情好肖粮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布孤页。 她就那樣靜靜地躺著,像睡著了一般涩馆。 火紅的嫁衣襯著肌膚如雪行施。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天凌净,我揣著相機(jī)與錄音悲龟,去河邊找鬼。 笑死冰寻,一個(gè)胖子當(dāng)著我的面吹牛须教,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斩芭,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼轻腺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了划乖?” 一聲冷哼從身側(cè)響起贬养,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琴庵,沒想到半個(gè)月后误算,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迷殿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年儿礼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庆寺。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚊夫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懦尝,到底是詐尸還是另有隱情知纷,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布陵霉,位于F島的核電站琅轧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踊挠。R本人自食惡果不足惜乍桂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧模蜡,春花似錦、人聲如沸扁凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谨朝。三九已至卤妒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間字币,已是汗流浹背则披。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洗出,地道東北人士复。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像翩活,于是被迫代替她去往敵國(guó)和親阱洪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 參考自滾滾貓的《iOS APP生命周期 和 UIViewController的生命周期》菠镇,滄州寧少的《iOS Ap...
    小暖風(fēng)閱讀 2,886評(píng)論 0 6
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,105評(píng)論 1 32
  • load初始化方法<加載到內(nèi)存就會(huì)執(zhí)行冗荸,不需要觸發(fā),且只會(huì)調(diào)用一次> + (void)load 只要加載內(nèi)存中就會(huì)...
    flowerflower閱讀 728評(píng)論 1 2
  • ViewController生命周期 按照?qǐng)?zhí)行順序排列: initWithCoder:通過nib文件初始化時(shí)觸發(fā)利耍。...
    隱身人閱讀 688評(píng)論 3 7
  • 譯者注:本文是對(duì) Apple 官方文檔的翻譯蚌本,原文地址為:https://developer.apple.com/...
    ampire_dan閱讀 7,436評(píng)論 0 13