UIViewController 的生命周期
答:來源:http://www.cnblogs.com/dahe007/p/6002964.html
一水醋、下面帶 (NSObject)的方法是NSObject提供的方法捻勉。其他的都是UIViewController 提供的方法。
load (NSObject)
initialize (NSObject)
init (NSObject)
initWithCoder
initWithNibName
awakeFromNib (NSObject)
loadView
viewDidLoad
viewWillAppear
updateViewConstraints
viewWillLayoutSubviews
viewDidLayoutSubviews
viewDidAppear
viewWillDisappear
viewDidDisappear
dealloc (NSObject)
didReceiveMemoryWarning
二道伟、load、initialize晨继、init
load俺孙、initialize是繼承的NSObject的方法。這些在其他文章里寫過墓卦,就不詳述了倦春。大體說下。
在main方法還沒執(zhí)行的時(shí)候 就會(huì) 加載所有類落剪,調(diào)用所有類的load方法睁本。
一般會(huì)在load中實(shí)現(xiàn)Method Swizzle。
初始化對(duì)象忠怖,調(diào)用alloc的時(shí)候會(huì)調(diào)用initialize方法呢堰。這個(gè)時(shí)候是分配內(nèi)存。
init方法是在內(nèi)存中創(chuàng)建好對(duì)象凡泣。
三枉疼、 initWithCoder、initWithNibName鞋拟、awakeFromNib
1骂维、initWithCoder、awakeFromNib
initWithCoder:反歸檔贺纲,如果對(duì)象是從文件解析來的 就會(huì)調(diào)用席舍。
awakeFromNib: 從xib或者storyboard加載完畢 會(huì)調(diào)用。
新建UIView的子類并且想在load nib的時(shí)候做一些初始化工作的時(shí)候 可以重寫awakeFromNib哮笆。bundle在load nib后會(huì)給每個(gè)view對(duì)象發(fā)送一個(gè)awakeFromNib消息来颤。
2、用storyboard稠肘,順序:
initialize -> initWithCoder -> awakeFromNib -> loadView
2福铅、用Xib或者純代碼:
如果用[[VC alloc] init] 來初始化:
initialize -> init -> initWithNibName -> loadView
如果用[[VC alloc] initWithNibName:@“VC” bundle:nil] 來初始化:
initialize -> initWithNibName -> loadView
使用Xib來實(shí)現(xiàn)VC的時(shí)候,不要重寫loadView方法项阴。如果重寫了loadView方法滑黔,則Xib中View就會(huì)消失笆包,變成空View。
四略荡、loadView
1庵佣、要重新設(shè)置View的時(shí)候,在loadView中去創(chuàng)建UIViewController的view汛兜,也就是self.view巴粪。
在這個(gè)方法中主要完成一些關(guān)鍵view的初始化工作。加載成功后接著調(diào)用viewDidLoad方法粥谬。
在loadView之前肛根,self.view是不存在的,也就是說view還沒有被初始化漏策,loadView完成后view就建立好了派哲。
比如:
- (void)loadView {
NSLog(@"loadView中調(diào)用self.view : %@", self.view);
}
這樣會(huì)直接崩潰,因?yàn)閟elf.view 還沒創(chuàng)建掺喻。
在重寫的loadView中調(diào)用[super loadView]芭届,會(huì)自動(dòng)初始化self.view。如下方式就不會(huì)崩潰:
- (void)loadView {
[super loadView];
NSLog(@"loadView中調(diào)用self.view : %@", self.view);
}
2感耙、每次訪問UIViewController的view(比如vc.view喉脖、self.view)而且view為nil,loadView方法就會(huì)被調(diào)用抑月。
- (void)loadView {
[super loadView];
NSLog(@"loadView中調(diào)用self.view : %@", self.view);
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view = nil;
self.view.backgroundColor = [UIColor redColor];
//這時(shí)候view為nil,會(huì)再次調(diào)用loadView方法舆蝴。執(zhí)行完 loadView后繼續(xù)執(zhí)行viewDidLoad方法谦絮,這樣就死循環(huán)了。
}
上面代碼 直接就死循環(huán)了洁仗,一直執(zhí)行l(wèi)oadView和viewDidLoad方法层皱。
3、loadView中自定義了view赠潦,那么xib叫胖、storyboard中設(shè)置的頁面就會(huì)失效,frame也是無效的她奥。如:
- (void)loadView {
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
customView.backgroundColor = [UIColor redColor];
self.view = customView;
}
上面代碼執(zhí)行的效果是 全屏都是紅色瓮增。
4、修改self.view的大小哩俭,可以在 viewDidLayoutSubviews 方法里去修改:
- (void)viewWillLayoutSubviews {
self.view.frame = CGRectMake(0, 0, 150, 150);
}
5绷跑、默認(rèn)的[super loadView] 中做了哪些事情:
5.1、如果UIViewController制定xib的名字或者storyboard 進(jìn)行初始化 凡资,這樣就會(huì)根據(jù)傳入的xib的名字 去初始化view砸捏。
5.2、如果xib沒有顯示的指定名稱,就默認(rèn)加載和UIViewController同名的xib文件垦藏。
5.3梆暖、如果沒有找到xib文件就會(huì)創(chuàng)建 一個(gè)空白的UIView,這個(gè)view的frame為屏幕的大小掂骏。
五轰驳、viewDidLoad
loadView執(zhí)行完成后調(diào)用viewDidLoad,viewDidLoad中主要完成界面的初始化 芭挽。如:往view上添加子視圖滑废、讀取數(shù)據(jù)等。
頁面打開后袜爪,如果沒有銷毀 就只執(zhí)行一次蠕趁。
六、viewWillAppear
viewDidLoad執(zhí)行完成后就執(zhí)行viewWillAppear辛馆。每次打開頁面都會(huì)執(zhí)行viewWillAppear俺陋。比如:從A頁面push到B頁面,然后從B頁面返回到A頁面的時(shí)候昙篙,viewDidLoad就不再執(zhí)行腊状,而viewWillAppear還是會(huì)執(zhí)行。
如果要求每次顯示該頁面的時(shí)候都要刷新網(wǎng)絡(luò)數(shù)據(jù)苔可,就可以在viewWillAppear做網(wǎng)絡(luò)請(qǐng)求的操作缴挖。
七、updateViewConstraints
主要功能是更新view的約束,并會(huì)調(diào)用其所有子視圖的該方法去更新約束焚辅。
ViewController的View在更新視圖布局時(shí)映屋,會(huì)先調(diào)用ViewController的updateViewConstraints 方法。我們可以通過重寫這個(gè)方法去更新當(dāng)前View的內(nèi)部布局同蜻,而不用再繼承這個(gè)View去重寫-updateConstraints方法棚点。
兩個(gè)方法都需要在方法實(shí)現(xiàn)的最后調(diào)用父類的該方法。并且這兩個(gè)方法不建議直接調(diào)用湾蔓。
- (void)updateViewConstraints {
//在這里給view添加約束瘫析,請(qǐng)確保該view的translatesAutoresizingMaskIntoConstraints屬性已設(shè)置為false
[super updateViewConstraints];
}
八、viewWillLayoutSubviews
view即將布局其Subviews默责。比如view的bounds改變了贬循,要調(diào)整Subviews的位置,在調(diào)整之前要做的一些工作就可以在該方法中實(shí)現(xiàn)桃序。
我們可以在這里設(shè)置 subviews 的 frame 屬性甘有。
在 Autolayout 機(jī)制被調(diào)用之前,viewWillLayoutSubviews 會(huì)被調(diào)用葡缰,在 Autolayout 機(jī)制被調(diào)用之后亏掀,viewDidLayoutSubviews 會(huì)被調(diào)用忱反。即在 viewWillLayoutSubviews 和 viewDidLayoutSubviews 之間,Autolayout 機(jī)制會(huì)被調(diào)用滤愕。所以從viewWillLayoutSubviews 中獲取的frame 是老的frame温算,從viewDidLayoutSubviews獲取的frame才是正確的frame。
比如xib中設(shè)置了約束间影,然后 在viewDidLoad中設(shè)置frame注竿,真實(shí)的位置還是根據(jù)xib中的約束顯示的。
而且在viewWillLayoutSubviews中修改frame也是不生效的魂贬,那么巩割,如何才能用代碼修改布局生效?
方法一:
在viewDidLayoutSubviews中修改frame付燥,這是最簡單的方法宣谈。
方法二:推薦做法
在updateConstraints中修改約束。
方法三:
在viewWillLayoutSubviews中修改約束
九键科、viewDidLayoutSubviews
控制器的view布局子控件完成闻丑。這里獲取的frame才是最正確的frame。
如果控件用的約束來布局的勋颖,在viewDidLayoutSubviews 中 去設(shè)置視圖的frame 是無效的嗦嗡。
如果控件是用frame來布局的,在viewDidLayoutSubviews 中 去設(shè)置視圖的frame 是有效的饭玲。
self.view 在viewDidLayoutSubviews 中 去設(shè)置 frame 是有效的侥祭。
十、viewWillDisappear
控制器的view即將消失的時(shí)候
十一茄厘、viewDidDisappear
控制器的view完全消失的時(shí)候
十二矮冬、 dealloc
當(dāng)對(duì)象被釋放的時(shí)候調(diào)用dealloc。
ARC中dealloc方法中常用的一些作用:
在dealloc中移除掉notification蚕断,解除delegate關(guān)系。
看一個(gè)類中是否有循環(huán)引用入挣,可以用Instrument或者FBMemoryProfiler亿乳,不過最簡單的還是在dealloc里打印日志。
十三径筏、didReceiveMemoryWarning
當(dāng)系統(tǒng)內(nèi)存不足時(shí)葛假,VC的didReceiveMemoryWarining 方法會(huì)被調(diào)用,VC所在的導(dǎo)航棧(self.navigationController.viewControllers)中的VC 也會(huì)被調(diào)didReceiveMemoryWarining滋恬。
didReceiveMemoryWarining 會(huì)判斷當(dāng)前VC的view是否顯示在window上聊训,
如果沒有顯示在window上,則didReceiveMemoryWarining 會(huì)自動(dòng)將VC 的view以及其所有子view全部銷毀恢氯。
如果當(dāng)前UIViewController的view顯示在window上带斑,則不銷毀該viewcontroller的view鼓寺。
AppStore中一些可以釋放內(nèi)存的APP,就是先不斷增加使用的內(nèi)存勋磕,然后系統(tǒng)檢測到內(nèi)存不足的時(shí)候 會(huì)自動(dòng)進(jìn)行回收妈候。
擴(kuò)展:http://www.reibang.com/p/a5f82922e387
loadView方法
當(dāng)我們用到控制器view時(shí),就會(huì)調(diào)用控制器view的get方法挂滓,在get方法內(nèi)部苦银,首先判斷view是否已經(jīng)創(chuàng)建,如果已存在赶站,則直接返回存在的view幔虏,如果不存在,則調(diào)用控制器的loadView方法贝椿,在控制器沒有被銷毀的情況下想括,loadView也可能會(huì)被執(zhí)行多次
viewDidLoad方法
當(dāng)控制器的loadView方法執(zhí)行完畢,view被創(chuàng)建成功后团秽,就會(huì)執(zhí)行viewDidLoad方法主胧,該方法與loadView方法一樣,也有可能被執(zhí)行多次习勤。在開發(fā)中踪栋,我們可能從未遇到過執(zhí)行多次的情況,那什么時(shí)候會(huì)執(zhí)行多次呢图毕?
比如A控制器push出B控制器夷都,此時(shí),窗口顯示的是B控制器的view予颤,此時(shí)如果收到內(nèi)存警告囤官,我們一般會(huì)將A控制器中沒用的變量及view銷毀掉,之后當(dāng)我們從B控制器pop到A控制器時(shí)蛤虐,就會(huì)再次執(zhí)行A控制器的loadView方法與viewDidLoad方法党饮。
控制器view的加載
1.通過storyboard加載
當(dāng)控制器通過storyboard加載時(shí),需要指定storyboard的名稱驳庭,控制器view最終就是storyboard所描述的樣子刑顺。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"TestViewController" bundle:nil];
TestViewController *testVC = [storyboard instantiateInitialViewController];
[self.navigationController pushViewController:testVC animated:YES];
}
2.通過xib加載當(dāng)控制器view通過xib加載的時(shí)候,可能會(huì)出現(xiàn)三種情況
a. 指定xib名稱(OtherViewController.xib)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
TestViewController *testVC = [[TestViewController alloc] initWithNibName:@"OtherViewController" bundle:nil];
[self.navigationController pushViewController:testVC animated:YES];
}
當(dāng)我們指定了xib的名稱饲常,loadView方法就會(huì)去加載對(duì)應(yīng)的xib(OtherViewController.xib)
b.不指定xib名稱1
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
TestViewController *testVC = [[TestViewController alloc] init];
[self.navigationController pushViewController:testVC animated:YES];
}
如果我們不指定xib名稱蹲堂,loadView就會(huì)加載與控制器同名的xib(TestViewController.xib)
c.不指定xib名稱2
我們先將TestViewController.xib這個(gè)文件刪除掉。
當(dāng)沒有指定xib名稱贝淤,且沒有與控制器同名的xib時(shí)柒竞,會(huì)加載前綴與控制器名相同而不帶controller的xib(TestView.xib)。
3.不通過sb\xib加載
將TestView.xib這個(gè)文件也刪除掉播聪,再來運(yùn)行程序朽基,結(jié)果是黑色的布隔。
控制器view是存在的,只不過顏色為clearColor踩晶,所以看到的黑色其實(shí)是UIWindow的执泰。
4.重寫loadView方法
我們重寫TestViewController的loadView方法,里面不做任何事
- (void)loadView { }
結(jié)果跟上面一樣黑渡蜻,不同的是术吝,這次并沒有創(chuàng)建view,最外層并不是UIView
如果我們希望控制器view加載出來的時(shí)候不是UIView而是其他控件茸苇,比如UIImageView排苍,那我們就可以重寫loadView
- (void)loadView
{
self.view = [[UIImageView alloc] init];
}
結(jié)論
1.重寫loadView方法,則會(huì)根據(jù)重寫的loadView方法創(chuàng)建view
2.控制器通過storyboard加載学密,則根據(jù)storyboard的描述創(chuàng)建view
3.控制器view通過xib加載淘衙,則根據(jù)nibName對(duì)應(yīng)的xib創(chuàng)建view
4.沒有指定nibName,則根據(jù)與控制器同名的xib創(chuàng)建view
5.沒有同名的xib腻暮,則根據(jù)與控制器名前綴相同不帶controller的xib創(chuàng)建view
6.如果都沒有彤守,則創(chuàng)建一個(gè)空白的xib
7.awakeFromNib當(dāng)控制器從nib加載的時(shí)候就會(huì)調(diào)用這個(gè)方法
8.storyboard加載的是控制器及控制器view,而xib加載的僅僅只是控制器的view