iOS对UIViewController生命周期和属性方法的解析
?
iOS對UIViewController生命周期和屬性方法的解析
一、引言
????????作為MVC設(shè)計模式中的C,Controller一直扮演著項目開發(fā)中最重要的角色,它是視圖和數(shù)據(jù)的橋梁,通過它的管理,將數(shù)據(jù)有條有理的展示在我們的View層上。iOS中的UIViewController是UIKit框架中最基本的一個類。從第一個UI視圖到復(fù)雜完整項目,都離不開UIViewController作為基礎(chǔ)。基于UIViewController的封裝和擴展,也能夠出色的完成各種復(fù)雜界面邏輯。這篇博客,旨在討論UIViewController的生命周期和屬性方法,在最基礎(chǔ)的東西上,往往會得到意想不到的驚喜。
二、UIViewController的生命周期
????????要了解UIViewController,先要弄清楚其生命周期。在面向?qū)ο蟮恼Z言中,是對象,就一定要有生命周期,UIViewController也不例外,生命周期管理Controller的作用范圍和時間,也管理其內(nèi)對象的作用范圍和時間。首先,UIViewController中與其生命周期有關(guān)的幾個函數(shù)如下:
//類的初始化方法 +?(void)initialize; //對象初始化方法 -?(instancetype)init; //從歸檔初始化 -?(instancetype)initWithCoder:(NSCoder?*)coder; //加載視圖 -(void)loadView; //將要加載視圖 -?(void)viewDidLoad; //將要布局子視圖 -(void)viewWillLayoutSubviews; //已經(jīng)布局子視圖 -(void)viewDidLayoutSubviews; //內(nèi)存警告 -?(void)didReceiveMemoryWarning; //已經(jīng)展示 -(void)viewDidAppear:(BOOL)animated; //將要展示 -(void)viewWillAppear:(BOOL)animated; //將要消失 -(void)viewWillDisappear:(BOOL)animated; //已經(jīng)消失 -(void)viewDidDisappear:(BOOL)animated; //被釋放 -(void)dealloc;上面這么多的函數(shù),乍一看什么復(fù)雜,其實關(guān)系什么明朗,除了initialize,init和initWithCoder不是存在所有對象的聲明周期中,其他函數(shù)都會在UIViewController的聲明周期中有序的被調(diào)用。那么具體的調(diào)用順序是怎樣的呢,最好的辦法是實踐一下,通過編號打印,結(jié)果如下:
這是一個ViewController完整的聲明周期,其實里面還有好多地方需要我們注意一下:
1:initialize函數(shù)并不會每次創(chuàng)建對象都調(diào)用,只有在這個類第一次創(chuàng)建對象時才會調(diào)用,做一些類的準(zhǔn)備工作,再次創(chuàng)建這個類的對象,initalize方法將不會被調(diào)用,對于這個類的子類,如果實現(xiàn)了initialize方法,在這個子類第一次創(chuàng)建對象時會調(diào)用自己的initalize方法,之后不會調(diào)用,如果沒有實現(xiàn),那么它的父類將替它再次調(diào)用一下自己的initialize方法,以后創(chuàng)建也都不會再調(diào)用。因此,如果我們有一些和這個相關(guān)的全局變量,可以在這里進(jìn)行初始化。
2:init方法和initCoder方法相似,只是被調(diào)用的環(huán)境不一樣,如果用代碼進(jìn)行初始化,會調(diào)用init,從nib文件或者歸檔進(jìn)行初始化,會調(diào)用initCoder。
3:loadView方法是開始加載視圖的起始方法,除非手動調(diào)用,否則在ViewController的生命周期中沒特殊情況只會被調(diào)用一次。
4:viewDidLoad方法是我們最常用的方法的,類中成員對象和變量的初始化我們都會放在這個方法中,在類創(chuàng)建后,無論視圖的展現(xiàn)或消失,這個方法也是只會在將要布局時調(diào)用一次。
5:viewWillAppare:視圖將要展現(xiàn)時會調(diào)用。
6:viewWillLayoutSubviews:在viewWillAppare后調(diào)用,將要對子視圖進(jìn)行布局。
7:viewDidLayoutSubviews:已經(jīng)布局完成子視圖。
8:viewDidAppare:視圖完成顯示時調(diào)用。
9:viewWillDisappare:視圖將要消失時調(diào)用。
10:viewDidDisappare:視圖已經(jīng)消失時調(diào)用。
11:dealloc:controller被釋放時調(diào)用。
注意:經(jīng)過測試,從nib文件加載的controller,只要不釋放,在每次viewWillAppare時都會調(diào)用layoutSubviews方法,有時甚至?xí)趘iewDidAppare后在調(diào)用一次layoutSubviews,而重點是從代碼加載的則只會在開始調(diào)用一次,之后都不會,所以注意,在layoutSubviews中寫相關(guān)的布局代碼十分危險。
三、從storyBoard加載UIViewController實例的傳值陷阱
????????我們知道,當(dāng)我們從StoryBoard中加載ViewController時,我們在Controller中拖拽的視圖是可以被初始化的,這里面有一點需要我們注意,如果我們需要向controller中視圖進(jìn)行傳值設(shè)置,通過以下方法得到的Controller中,視圖還沒有被初始化創(chuàng)建出來:
?ViewController2?*?viewController2?=?[[UIStoryboard?storyboardWithName:@"Main"?bundle:[NSBundle?mainBundle]]?instantiateViewControllerWithIdentifier:@"ViewController2"];我們可以在ViewController2的storyBoard中拉一個label,然后關(guān)聯(lián)到頭文件中,如下打印,會發(fā)現(xiàn)我們得到controller時,里面的視圖對象并沒有進(jìn)行創(chuàng)建:
ViewController2?*?viewController2?=?[[UIStoryboard?storyboardWithName:@"Main"?bundle:[NSBundle?mainBundle]]?instantiateViewControllerWithIdentifier:@"ViewController2"];NSLog(@"%@",viewController2.label);[self?presentViewController:viewController2?animated:YES?completion:nil];打印如下:
可以想象,如果我們這時候需要對label進(jìn)行一些屬性設(shè)置,必然失敗。有人提出可以在創(chuàng)建后,手動調(diào)以下loadView方法,我們試一下,結(jié)果如下:
可以看到,手動調(diào)用loadView后,label是被創(chuàng)建了出來,但是暴漏了一個更嚴(yán)重的問題,系統(tǒng)不在調(diào)用ViewDidLoad方法,這是十分有風(fēng)險的,因為我們大部分的初始化代碼都會放在這個方法里,所以手動調(diào)用loadView是一種錯誤的方法,apple文檔聲明對于loadView方法,我們從來都不要手動直接調(diào)用,那么我們?nèi)绾螌崿F(xiàn)創(chuàng)建后對成員對象進(jìn)行傳值設(shè)置呢,iOS9中增加了這樣一個方法:
-?(void)loadViewIfNeeded?NS_AVAILABLE_IOS(9_0);這個方法十分有用,調(diào)用這個方法,會將視圖創(chuàng)建出來,并且不會忽略viewDidLoad的調(diào)用。
在iOS9中,UIViewController還增加了下面一個布爾值的屬性,可以同來判斷controller的view是否已經(jīng)加載完成:
@property(nullable,?nonatomic,?readonly,?strong)?UIView?*viewIfLoaded?NS_AVAILABLE_IOS(9_0);四、UIViewController與StroyBoard的相關(guān)相互方法
????????對于ViewConroller,我們一般有兩種方式創(chuàng)建,一種是用純代碼的方式,一種是與StoryBoard關(guān)聯(lián),在UIViewController中,有許多方法方便我們與StoryBoard進(jìn)行交互聯(lián)系。
1、ViewController直接在StoryBoard中進(jìn)行跳轉(zhuǎn)的傳值
????????在StoryBoard中進(jìn)行界面跳轉(zhuǎn)是十分方便的,我們在StoryBoard中拉入兩個ViewController,在一個上面添加一個按鈕,點住按鈕按住control,將鼠標(biāo)拉到第二個controller上,會出現(xiàn)如下的跳轉(zhuǎn)選項:
我們選擇一個后,就會在兩個controller之間建立一個跳轉(zhuǎn)連接。當(dāng)我們運行點擊按鈕后,會自動從第一個controller跳轉(zhuǎn)到第二個controller。在UIViewController中有如下方法可以對是否跳轉(zhuǎn)進(jìn)行控制:
-?(BOOL)shouldPerformSegueWithIdentifier:(NSString?*)identifier?sender:(nullable?id)sender?NS_AVAILABLE_IOS(6_0);這個方法如果返回NO,自動跳轉(zhuǎn)將不能進(jìn)行,會被拒絕,需要注意的是,這個方法只會在自動的跳轉(zhuǎn)時被調(diào)用,我們手動使用代碼跳轉(zhuǎn)StoryBoard中的連接關(guān)系時是不會被調(diào)用的,我們后面討論。
????????在執(zhí)行過上述方法后,如果返回YES,系統(tǒng)還會在執(zhí)行如下一個方法,作為跳轉(zhuǎn)前的準(zhǔn)備,我們可以在這個方法中進(jìn)行一些傳值操作,這個方法無論使我們手動進(jìn)行跳轉(zhuǎn)還是storyboard中自動跳轉(zhuǎn),都會被執(zhí)行:
-?(void)prepareForSegue:(UIStoryboardSegue?*)segue?sender:(nullable?id)sender?NS_AVAILABLE_IOS(5_0);sugur對象中封裝了相關(guān)的ViewController,可以使用segue.destinationViewController獲取。
????????segue在StoryBoard中除了用來自動正向跳轉(zhuǎn)外,我們還可以進(jìn)行反向的跳轉(zhuǎn),類似pop和dismiss方法,這種segue被稱為unwind sugue。例如,我們有一個controller1和一個controllert2,要使用unwind segue從2返回1,我們需要在2中實現(xiàn)如下格式的方法:
-?(IBAction)unwindSegueToViewController:(UIStoryboardSegue?*)segue?{NSLog(@"unwindSegueToViewController"); }這個方法中的返回值必須為IBAction,參數(shù)必須是UIStoryboardSegue,方法名我們可以自己定義,之后在StoryBoard中的ViewController1中的Exit選項中,我們會發(fā)現(xiàn)多了一個這樣的方法:
我們可以把它連接到viewController2中的一個按鈕上:
這樣,當(dāng)我們點擊viewController2中的按鈕時,就會返回到我們第一個ViewController1中了。
當(dāng)然,在使用unwind segue方法時,也是會有一些回調(diào)幫助我們進(jìn)行跳轉(zhuǎn)前的設(shè)置和傳值,UIViewController如下方法會在跳轉(zhuǎn)前調(diào)用,返回NO,則不能進(jìn)行跳轉(zhuǎn):
-(BOOL)canPerformUnwindSegueAction:(SEL)action?fromViewController:(UIViewController?*)fromViewController?withSender:(id)sender{NSLog(@"canPerformUnwindSegueAction");return?YES; }之后會執(zhí)行我們自定義的unwindSegue方法,這個方法中我們可以什么都不寫,模式是會進(jìn)行跳轉(zhuǎn)的。
2、使用代碼跳轉(zhuǎn)Storyboard中的controller
????????我們除了在Storyboard中拉拉扯扯可以進(jìn)行控制器的跳轉(zhuǎn)外,我們也可以使用代碼來跳轉(zhuǎn)Storyboard中segue連接關(guān)系。
????????在Storyboard中兩個控制器間建立一個segue聯(lián)系,我們可以取一個名字:
在觸發(fā)跳轉(zhuǎn)的方法中,使用如下方法進(jìn)行跳轉(zhuǎn),這里面的參數(shù)id就是我們?nèi)〉胹egue的id:
-?(void)performSegueWithIdentifier:(NSString?*)identifier?sender:(nullable?id)sender?NS_AVAILABLE_IOS(5_0);下面三個屬性我們可以獲取controller的nib文件名,其storyBoard和其Bundle:
@property(nullable,?nonatomic,?readonly,?copy)?NSString?*nibName;?? @property(nullable,?nonatomic,?readonly,?strong)?NSBundle?*nibBundle;? @property(nullable,?nonatomic,?readonly,?strong)?UIStoryboard?*storyboard?NS_AVAILABLE_IOS(5_0);五、UIViewController之間的一些從屬關(guān)系
????????這部分的內(nèi)容和方法可能我們接觸用到的并不多,但是在某些情況下,使用這些方法可以大大的方便某些邏輯。
1、parentViewController
????????UIViewController里面封裝了一個數(shù)組,可以存放其子ViewController,系統(tǒng)中使用的例子就是導(dǎo)航和tabBar這類的控制器,我們使用如下方法可以直接訪問這些父的controller:
@property(nullable,nonatomic,weak,readonly)?UIViewController?*parentViewController;2、模態(tài)跳轉(zhuǎn)中Controller的從屬
????????在我們進(jìn)行控制器的跳轉(zhuǎn)時,只要控制器沒有被釋放,我們都可以順藤摸瓜的找到它,使用如下兩個方法:
//其所present的contller,比如,A和B兩個controller,A跳轉(zhuǎn)到B,那么A的presentedViewController就是B @property(nullable,?nonatomic,readonly)?UIViewController?*presentedViewController??NS_AVAILABLE_IOS(5_0); //和上面的方法剛好相反,比如,A和B兩個controller,A跳轉(zhuǎn)到B,那么B的presentingViewController就是A @property(nullable,?nonatomic,readonly)?UIViewController?*presentingViewController?NS_AVAILABLE_IOS(5_0);了解了上面方法我們可以知道,對于反向傳值這樣的問題,我們根本不需要代理,block,通知等這樣的復(fù)雜手段,只需要獲取跳轉(zhuǎn)到它的Controller,直接設(shè)置即可。舉個例子,我們需要在第二個界面消失后,改變第一個界面的顏色,在第二個controller中只需要下面的代碼即可實現(xiàn)?:
????self.presentingViewController.view.backgroundColor?=?[UIColor?colorWithRed:arc4random()%255/255.0?green:arc4random()%255/255.0?blue:arc4random()%255/255.0?alpha:1];[self?dismissViewControllerAnimated:YES?completion:nil];六、UIViewController的模態(tài)跳轉(zhuǎn)及動畫特效
????????單純的UIViewController中,我們使用最多的是如下的兩個方法,一個向前跳轉(zhuǎn),一個向后返回:
-?(void)presentViewController:(UIViewController?*)viewControllerToPresent?animated:?(BOOL)flag?completion:(void?(^?__nullable)(void))completion?NS_AVAILABLE_IOS(5_0); -?(void)dismissViewControllerAnimated:?(BOOL)flag?completion:?(void?(^?__nullable)(void))completion?NS_AVAILABLE_IOS(5_0);從方法中,我們可以看到,有animated這個參數(shù),來選擇是否有動畫特效,默認(rèn)的動畫特效是像抽屜一樣從手機屏幕的下方向上彈起,當(dāng)然,這個效果我們可以進(jìn)行設(shè)置,UIViewController有如下一個屬性來設(shè)置動畫特效:
@property(nonatomic,assign)?UIModalTransitionStyle?modalTransitionStyle?NS_AVAILABLE_IOS(3_0);注意,這個要設(shè)置的是將要跳轉(zhuǎn)到的controller,枚舉如下:
typedef?NS_ENUM(NSInteger,?UIModalTransitionStyle)?{UIModalTransitionStyleCoverVertical?=?0,//默認(rèn)的,從下向上覆蓋UIModalTransitionStyleFlipHorizontal?,//水平翻轉(zhuǎn)UIModalTransitionStyleCrossDissolve,//溶解UIModalTransitionStylePartialCurl?,從下向上翻頁 };除了跳轉(zhuǎn)的效果,還有一個屬性可以設(shè)置彈出的controler的填充效果,但是這個屬性只在pad上有效,在iphone上無效,都是填充到整個屏幕:
@property(nonatomic,assign)?UIModalPresentationStyle?modalPresentationStyle?NS_AVAILABLE_IOS(3_2); //枚舉如下 typedef?NS_ENUM(NSInteger,?UIModalPresentationStyle)?{UIModalPresentationFullScreen?=?0,//填充整個屏幕UIModalPresentationPageSheet,//留下狀態(tài)欄UIModalPresentationFormSheet,//四周留下變暗的空白UIModalPresentationCurrentContext?,//和跳轉(zhuǎn)到它的控制器保持一致UIModalPresentationCustom?NS_ENUM_AVAILABLE_IOS(7_0),//自定義UIModalPresentationOverFullScreen?NS_ENUM_AVAILABLE_IOS(8_0),UIModalPresentationOverCurrentContext?NS_ENUM_AVAILABLE_IOS(8_0),UIModalPresentationPopover?NS_ENUM_AVAILABLE_IOS(8_0)?__TVOS_PROHIBITED,UIModalPresentationNone?NS_ENUM_AVAILABLE_IOS(7_0)?=?-1,????????? };? ? ? ? ? ? ? ? ? ? ? ?
轉(zhuǎn)載于:https://www.cnblogs.com/maojunjun/p/4966634.html
總結(jié)
以上是生活随笔為你收集整理的iOS对UIViewController生命周期和属性方法的解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PuTTY 命令行改进 有效解决
- 下一篇: GPUImage API 文档之GPUI