iOS學習筆記02—View Controller的生命週期
一、ViewController
View Controller用於管理應用的資源,包括管理與之關聯的View,與其他ViewController通信和協調。爲了保證程序的高效運行,View Controller總是在需要的時候才加載View(通常被稱爲lazyload),並在不需要或者內存告警的時候卸載視圖。
二、ViewController的生命週期
用一個自定義的ViewController做測試類名TestViewController,重寫如下方法並加上日誌信息:
- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNilbundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSLog(@"initWithNibName");
}
return self;
}
-(void)dealloc{
[super dealloc];
NSLog(@"dealloc");
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear: animated];
NSLog(@"viewDidAppear");
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear: animated];
NSLog(@"viewDidDisappear");
}
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
NSLog(@"viewDidLayoutSubviews");
}
-(void)viewDidLoad{
[super viewDidLoad];
NSLog(@"viewDidLoad");
}
-(void)viewDidUnload{
[super viewDidUnload];
NSLog(@"viewDidUnload");
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear: animated];
NSLog(@"viewWillAppear");
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear: animated];
NSLog(@"viewWillDisappear");
}
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
NSLog(@"viewWillLayoutSubviews");
}
-(void)viewWillUnload{
[super viewWillUnload];
NSLog(@"viewWillUnload");
}
-(void)loadView{
[super loadView];
NSLog(@"loadView");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can berecreated.
NSLog(@"didReceiveMemoryWarning");
}
在另外一個ViewController中調用代碼如下:
- (IBAction)buttonPressed:(id)sender {
TestViewController *viewController =[[TestViewController alloc] initWithNibName: @"TestViewController" bundle: nil];
[self.navigationController pushViewController:viewController animated: YES];
[viewController release];
}
點擊button,然後按導航欄的[返回],打印的日誌如下,顯示了各方法其調用順序:
2013-03-27 16:58:48.277StudyInit[1659:11303] initWithNibName
2013-03-27 16:58:48.279StudyInit[1659:11303] loadView
2013-03-27 16:58:48.280StudyInit[1659:11303] viewDidLoad
2013-03-27 16:58:48.281StudyInit[1659:11303] viewWillAppear
2013-03-27 16:58:48.284StudyInit[1659:11303] viewWillLayoutSubviews
2013-03-27 16:58:48.285StudyInit[1659:11303] viewDidLayoutSubviews
2013-03-27 16:58:48.637StudyInit[1659:11303] viewDidAppear
2013-03-27 16:58:50.158StudyInit[1659:11303] viewWillDisappear
2013-03-27 16:58:50.511StudyInit[1659:11303] viewDidDisappear
2013-03-27 16:58:50.512StudyInit[1659:11303] dealloc
在 iOS 模擬器的菜單裏選 硬件->模擬內存警告,打印的日誌信息如下:
2013-03-28 10:03:21.225StudyInit[556:11303] Received memory warning.
2013-03-28 10:03:21.226StudyInit[556:11303] didReceiveMemoryWarning
三、詳細解釋一下:
1、initWithNibName:
初始化ViewController本身。
init中應該只有相關數據的初始化,而且這些數據都是比較關鍵的數據,不要出現創建view的代碼,也不要調self.view,否則會導致ViewController創建view。
如果是外部通過調用initWithNibName:bundle指定nib文件名的話,ViewController記載此nib來創建View。
如果initWithNibName:bundle的name參數爲nil,則ViewController會通過以下兩個步驟找到與其關聯的nib。
1)如果ViewController的類名以“Controller”結尾,例如ViewController的類名是MyViewController,則查找是否存在MyView.nib;
2)找跟ViewController類名一樣的文件,例如MyViewController,則查找是否存在MyViewController.nib。
2、loadView:
只初始化view,一般用於創建比較關鍵的view如tableView Controller的tabView,UINavigationController的navgationBar,不可調用view的getter(在掉super loadView前),最好也不要初始化一些非關鍵的view。
需要用到view,比如調用了view的getter方法時,在getter裏會先判斷view是否創建,如果沒有創建,那麼會調用loadView來創建view,並賦值給ViewController的view屬性,在loadView之前view是nil。
如果子類沒有重寫的loadView,則ViewController會從StroyBoards中找或者調用其默認的loadView,默認的loadView返回一個空白的UIView對象。
View Controller是判斷子類是否重寫了loadView,而不是判斷調用子類的loadView之後ViewController的View是否爲空。就是說,如果子類重寫了loadView的話,不管子類在loadView裏面能否獲取到View,ViewController都會直接調viewDidLoad完成View的加載。
如果手工維護views,必須重載該方法。
如果使用IB維護views,不能重載該方法。
If you use Interface Builder to create your views and initialize
the view controller, you must not override this method.
3、viewDidLoad:
view已經加載到內存中了。適合創建一些附加的view和控件。
4、viewWillAppear:
view即將顯示,但此時其superView還爲nil,即view還沒有加到任何其他view中,但準備要添加到其他View中了。
在view被添加到superview之前,切換動畫之前調用。在這裏可以進行一些顯示前的處理。比如鍵盤彈出,一些特殊的過程動畫(比如狀態條和navigationbar顏色)。
5、viewWillLayoutSubviews:
view即將佈局其Subviews。比如view的bounds改變了,要調整Subviews的位置,在調整之前要做的一些工作就可以在該方法中實現。
6、viewDidLayoutSubviews:
view已經佈局其Subviews。比如view的bounds改變了,已經調整Subviews的位置,在調整完成之後要做的一些工作就可以在該方法中實現。
7、viewDidAppear:
view已經顯示,即已經加到其superView中了。view顯示後,在切換動畫後,如果有需要的操作,可以在這裏加入相關代碼
8、viewWillDisappear:
view即將從superView中移除,此時還沒有調用removeFromeSuperView。
9、viewDidDisappear:
view已經從superView中移除了。
10、dealloc:
view Controller被釋放。
需要注意如下幾個方法
viewDidUnload(iOS6廢棄了該方法)
viewWillUnload(iOS6廢棄了該方法)
didReceiveMemoryWarning
內存吃緊時(系統發出警告或者ViewController本身調用導致didReceiveMemoryWarning被調用)。
注意是除當前正在展示的view 所屬 viewController 以外所有已經在內存裏面的viewController 執行 didReceiveMemoryWarning方法,而不是當前viewController 執行 didReceiveMemoryWarning。
一個didReceiveMemoryWarning的實現方案:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Addcode to clean up any of your own resources that are no longer necessary.
if ([self.view window] == nil)
{
// Add code to preserve data stored in the views that might be
// needed later.
// Add code to clean up other strong references to the view in
// the view hierarchy.
self.view = nil;
}
}
一個可能的loadView實現方案
-(void)loadView
{
CGRect applicationFrame =[[UIScreenmainScreen] applicationFrame];
UIView *contentView = [[UIViewalloc]initWithFrame:applicationFrame];
contentView.backgroundColor =[UIColordarkGrayColor];
self.view = contentView;
UILabel *lab =[[UILabelalloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
lab.text = @"HelloWorld";
[self.viewaddSubview:lab];
}
loadView雖然返回值爲空,但必須在函數體內對self.view進行賦值,否則會在建立該界面的時候收到如下的log信息:
Application windows areexpected to have a root view controller at the end of application launch