UIViewController的生命周期

單純?yōu)榱俗鲰?xiàng)目你要不太了解底層的知識(shí)可能對(duì)你項(xiàng)目的實(shí)現(xiàn)影響不是很大(前提是不追求極致的項(xiàng)目????)。但是如果不知道點(diǎn)UIViewController的知識(shí)你想做項(xiàng)目那就有點(diǎn)難了。 因?yàn)樽龅捻?xiàng)目離不開各種各樣的UIViewController,所以我就帶你聊聊UIViewController的生命周期也就是它的 “初始化” 到 "釋放" 的過程。

前言:

iOS中最離不開的就是UIViewController,為啥它這么重要就不介紹了。
生命周期簡單概括有如下幾步

初始化 --> 加載視圖 -->配置視圖 -->顯示 -->消失 -->銷毀

初始化方式

  • xib文件
  • 純代碼
  • storyboard加載
- (instancetype)init (這個(gè)在UIViewController的API中是沒列出的)
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil ;
- (instancetype)initWithCoder:(NSCoder *)aDecoder ;

以上代碼是官方API羅列出來的初始化方法下面一一介紹

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ;
  • nibNameOrNil --> nib的名字
  • nibBundleOrNil --> 查找nib文件的bundle名字,一般指定為nil 進(jìn)行自動(dòng)搜索

該方法的使用也有幾個(gè)情況


項(xiàng)目結(jié)構(gòu)
  • 情況一
    BBBViewController *VC = [[BBBViewController alloc] initWithNibName:@"BBBViewController" bundle:nil];

該情況也是大家的通常做法,就是nib的名字跟控制器的類名 “一致“ 看一下效果。

運(yùn)行代碼效果

  • 情況二
    BBBViewController *VC = [[BBBViewController alloc] initWithNibName:@"OtherViewController" bundle:nil];
    該情況是nib的名字跟控制器的類名 ”不一致“ 看一下效果。

    代碼運(yùn)行效果

  • 情況三
    BBBViewController *VC = [[BBBViewController alloc] initWithNibName:nil bundle:nil];

參數(shù)指定為nil的時(shí)候它的加載情況有3種:

  1. 如果有跟控制器類名一致的(也就是BBBViewController.xib)就加載該文件,這個(gè)效果跟上面介紹的(情況一)是一樣的。
  2. 如果沒有跟控制器類名一致的xib文件的時(shí)候,也會(huì)自動(dòng)匹配項(xiàng)目中的xib文件。但這個(gè)xib文件的名字一定是控制器類名去除掉Controller之后剩下的名字(例:BBBViewController控制器,那么會(huì)自動(dòng)匹配BBBView.xib文件),其中這個(gè)場(chǎng)景有一個(gè)關(guān)鍵點(diǎn)如果想以這樣的方式匹配xib文件那么你的控制器的名字一定是以ViewController結(jié)尾的(***ViewController)否則不能匹配到這個(gè)情況。
  3. 如果項(xiàng)目中沒有上述的2個(gè)情況,那么控制器就會(huì)無xib加載也就是創(chuàng)建一個(gè)空的view
- (instancetype)init;

這個(gè)方法跟調(diào)用BBBViewController *VC = [[BBBViewController alloc] initWithNibName:nil bundle:nil];效果一樣
唯一一個(gè)區(qū)別就是這個(gè)方法會(huì)首先調(diào)用一下 initWithNibName:bundle:方法然后再調(diào)用一次alloc] init]方法(initWithNibName:bundle:初始化控制器時(shí)候不走該方法)

- (instancetype)initWithCoder:(NSCoder *)aDecoder ;

如果ViewController是從storyboard初始化的話,那么會(huì)執(zhí)行上面這個(gè)方法,但是跟加載xib文件有一個(gè)不同就是從storyboard加載VC的時(shí)候還會(huì)走 - (void)awakeFromNib 這個(gè)方法

總結(jié)

  • 通常我們需要做的是在ViewController的初始化階段對(duì)用到的數(shù)據(jù)進(jìn)行初始化的操作,切記不要在這個(gè)位置對(duì)view進(jìn)行操作(例如self.view),因?yàn)檫@個(gè)時(shí)候VC還沒有初始化完成哪來的view, 如果這個(gè)時(shí)候手動(dòng)調(diào)用的話會(huì)導(dǎo)致整個(gè)VC的流程出錯(cuò)。
  • 通過上面的介紹知道VC的初始化代碼的幾種方式,尤其是加載xib的時(shí)候的情況。不過為了可閱讀性還是建議xib的文件名跟VC的類名保持一致。

- (void)loadView

初始化完成就要真正的加載VC的view了,VC的view加載是一種懶加載的方式,用到的時(shí)候會(huì)自動(dòng)調(diào)用。所以不要手動(dòng)調(diào)用該方法,這個(gè)方法在VC的整個(gè)過程中可能會(huì)被調(diào)用多次。

有2種情況我們是一定不要重寫這個(gè)方法的

  1. view是從xib文件中加載的
  2. view是從storyboard中加載

以上兩種情況如果你重寫了loadView方法那么你的文件上的布局統(tǒng)統(tǒng)作廢

總結(jié): 一般我們是不需要重寫這個(gè)方法的,除非你對(duì)VC的view有特殊的要求,系統(tǒng)默認(rèn)是給我創(chuàng)建一個(gè)空的view作為VC的視圖。如果你自己想創(chuàng)建了一個(gè)view作為VC的view,那么一定要把自定義的view賦值給VC.view否則將認(rèn)為VC的view是空值,這樣會(huì)使VC陷入死循環(huán)。

- (void)viewDidLoad

當(dāng)VC的view被創(chuàng)建完成之后也就是執(zhí)行完loadView以后執(zhí)行該方法。通常我們需要在這個(gè)方法里面來配置UI。因?yàn)檫@個(gè)時(shí)候view是確確實(shí)實(shí)的存在了,可以對(duì)其進(jìn)行一些子視圖的添加等。

注意: 該方法通常是執(zhí)行一次,但其實(shí)他跟loadView這個(gè)方法一樣也存在執(zhí)行多次的情況,通過上面的介紹我們知道如果這個(gè)界面的view被重新加載那么一定順其自然的執(zhí)行該方法。
舉例說明:A控制器 push B控制器 如果這時(shí)收到了內(nèi)存警告隨即會(huì)調(diào)用A控制器中的didReceiveMemoryWarning方法,如果你在A和B控制器的didReceiveMemoryWarning方法對(duì)沒用的變量釋放以及銷毀其view的操作(self.view = nil) 那么當(dāng)你從B返回A的時(shí)候因?yàn)锳的view被釋放了,所以還會(huì)重新走viewDidLoad的方法。這個(gè)場(chǎng)景主要取決于你對(duì)內(nèi)存警告時(shí)候的處理。

- (void)viewWillAppear && - (void)viewDidAppear

這兩個(gè)方法是成對(duì)的,所以一起介紹viewWillAppear正常是在viewDidLoad之后調(diào)用的,但是該方法不一定是在viewDidLoad之后立即被調(diào)用。調(diào)用該方法的時(shí)刻是view顯示的時(shí)候,而且隨著控制器Push / Present 以及對(duì)應(yīng)的Pop / Dismiss 等方法會(huì)使該方法執(zhí)行多次。

  • 舉例:
// 這個(gè)方法是不會(huì)調(diào)用viewWillAppear的,因?yàn)槟阒皇浅跏蓟薞C并沒有讓view顯示出來
BBBViewController *VC = [[BBBViewController alloc] init];
// 這個(gè)方法是不會(huì)調(diào)用viewWillAppear
// 雖然你調(diào)用了vc.view但沒有加到可以顯示的圖層同樣不用調(diào)用
BBBViewController *VC = [[BBBViewController alloc] init];
VC.view.frame = CGRectMake(100, 100, 100, 100);
// 這個(gè)方法是會(huì)調(diào)用viewWillAppear,因?yàn)関iew加載到了父視圖上
BBBViewController *VC = [[BBBViewController alloc] init];
VC.view.frame = CGRectMake(100, 100, 100, 100);
[AAAVC.view addSubview:VC.view];

注意 viewWillAppear執(zhí)行完了,未必會(huì)執(zhí)行viewDidAppear,文章后面會(huì)舉例說明。

- (void)viewWillLayoutSubviews && - (void)viewDidLayoutSubviews

來看一下官方給的解釋

// Called just before the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. 
- (void)viewWillLayoutSubviews ;
// Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. 
- (void)viewDidLayoutSubviews ;

這2個(gè)方法也是成對(duì)的,這段英文的注釋大家肯定是一目了然, 一個(gè)是在 VC.view 的layoutSubviews被調(diào)用之前,另一個(gè)是在layoutSubviews被調(diào)用之后。這里不介紹layoutSubviews方法的調(diào)用機(jī)制。

注意 如果你的布局使用autolayout的時(shí)候,我們可能需要在控制器中使用代碼來獲取一些視圖的frame,這個(gè)時(shí)候就需要注意一定要在viewDidLayoutSubviews中獲取,只有在viewDidLayoutSubviews中視圖的frame才是準(zhǔn)確的。

- (void)viewWillDisappear && - (void)viewDidDisappear

同樣成對(duì)的2個(gè)方法,當(dāng)界面消失的時(shí)候例如pop/dismiss 操作的時(shí)候會(huì)先后執(zhí)行這兩個(gè)方法。

    BBBViewController *VC = [[BBBViewController alloc] init];
    [self addChildViewController:VC];
    VC.view.frame = CGRectMake(100, 100, 100, 100);
    [self.view addSubview:VC.view];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [VC.view removeFromSuperview];
    });

上面這一段代碼雖然不是pop/dismiss 依然會(huì)執(zhí)行這兩個(gè)方法,所以這兩個(gè)方法被調(diào)用的時(shí)機(jī)就是VC.view消失的時(shí)候 (消失不等于隱藏)

注意 viewWillDisappear執(zhí)行完了,未必會(huì)執(zhí)行viewDidDisappear,文章后面會(huì)舉例說明。

- (void)didReceiveMemoryWarning

當(dāng)系統(tǒng)的內(nèi)存不足時(shí),會(huì)調(diào)用didReceiveMemoryWarining方法,我們需要做的就是釋放掉部分暫時(shí)不用的資源。例:

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    if (!self.view.window && self.isViewLoaded) {
         self.view = nil; //釋放掉view等操作
    }
}

- (void)dealloc

該方法只有在 ViewController 徹底被釋放的時(shí)候調(diào)用。例如pop/dismiss,如果VC中的代碼不夠規(guī)范引起了循環(huán)引用等問題,即使執(zhí)行了pop/dismiss并且界面也消失了但并沒有真的釋放。要注意這個(gè)方面。

控制器之間跳轉(zhuǎn)場(chǎng)景舉例

舉例說明:現(xiàn)在有AAA控制器 和 BBB控制器。模擬在AAA控制器push到BBB控制器中的場(chǎng)景。下面這段代碼寫在AAA控制器中:

BBBViewController *VC = [[BBBViewController alloc] initWithNibName:nil bundle:nil];
//BBBViewController *VC = [[BBBViewController alloc] init]; //使用這句代碼初始化也可以
[self.navigationController pushViewController:VC animated:YES];

當(dāng)我們執(zhí)行AAA push到 BBB的時(shí)候

  • initWithNibName:bundle:(BBBViewController執(zhí)行的)
  • viewDidLoad: (BBBViewController執(zhí)行的)
  • viewWillDisappear:(AAAViewController執(zhí)行的,將要消失)
  • viewWillAppear:(BBBViewController執(zhí)行的)
  • viewWillLayoutSubviews:(BBBViewController執(zhí)行的)
  • viewDidLayoutSubviews:(BBBViewController執(zhí)行的)
  • viewDidDisappear:(AAAViewController執(zhí)行的,已經(jīng)消失)
  • viewDidAppear:(BBBViewController執(zhí)行的)
    注意:上面提到的消失不是指的銷毀

當(dāng)我們執(zhí)行BBB pop返回到 AAA的時(shí)候

  • viewWillDisappear:(BBBViewController執(zhí)行的)
  • viewWillAppear:(AAAViewController執(zhí)行的)
  • viewDidDisappear:(BBBViewController執(zhí)行的)
  • viewDidAppear:(AAAViewController執(zhí)行的)
  • dealloc:(BBBViewController執(zhí)行的)

當(dāng)我們?cè)贐BB界面 用手勢(shì)向右拖動(dòng)返回 AAA的時(shí)候
當(dāng)我們?cè)贐BB界面手勢(shì)拖動(dòng)讓BBB界面和AAA界面都出現(xiàn)在屏幕上,但最后松開還是停留在BBB界面上這個(gè)時(shí)候的執(zhí)行代碼有一點(diǎn)不同,來看一下:

  • viewWillDisappear:(BBBViewController執(zhí)行的,將要消失的)
  • viewWillAppear:(AAAViewController執(zhí)行的,要返回到A也就是將要出現(xiàn)了)
  • viewWillDisappear:(AAAViewController執(zhí)行的,因?yàn)槲覀冏詈笸T贐上所以剛才的A還要執(zhí)行一次這個(gè)方法)
  • viewDidDisappear:(AAAViewController執(zhí)行的)
  • viewWillAppear:(BBBViewController執(zhí)行的,剛剛執(zhí)行了將要消失所以現(xiàn)在重新出現(xiàn)要執(zhí)行這個(gè)方法)
  • viewDidAppear:(BBBViewController執(zhí)行的,最終停留在B上)
最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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