一個ViewController,一般通過init或initWithNibName來加載。二者沒有什么不同,init最終還是要調用initWithNibName方法(除非這個ViewController沒有nib文件)。
我們經常在initWithNibName方法中初始化視圖,變量或者其他成員。這是最常見的initWithNibName方法寫法:
- (id)initWithNibName:(NSString *)nibNameOrNilbundle:(NSBundle *)nibBundleOrNil
{
??? self = [superinitWithNibName:nibNameOrNil bundle:nibBundleOrNil];
??? if (self) {
??????? label=[[UILabelalloc]initWithFrame:
??????????????????CGRectMake(0,0,160,160)];
??????? [self.viewaddSubview:label];
...
??? }
??? returnself;
}
在if語句中,包含了最常見的成員初始化代碼。
在這段代碼中,如果你向ViewController的視圖樹中加入一些新的UIView子類,比如上面的代碼:
[self.viewaddSubview:label];
這不會有什么問題。但是這會導致另一個方法的調用,即viewDidLoad方法。
viewDidLoad方法一般情況下只會在nib文件已載入內存(即視圖樹構建完成)之后調用。
但還有另一種情況,如果ViewController的view屬性被引用時,view=nil,也會導致nib的加載行為,從而也導致viewDidLoad方法的調用。如果你在initWithNibName方法一直不引用view屬性,則直至initWithNibName方法結束,viewDidLoad方法也不會觸發。
你也許奇怪,如果在代碼中你一直不引用這個ViewController的view屬性怎么辦?那么是不是viewDidLoad方法一直都不會調用了?
它會在ViewController對象第1次present時調用,比如你使用presentModalViewController或pushViewController方法彈出它。這兩個方法同時還會調用ViewController的appear方法(即viewWillAppear方法和viewDidAppear方法)。
viewDidLoad方法會比appear方法要早執行(appear方法會導致一個彈出動畫產生)。而且如果在present之前已經執行過viewDidLoad方法,則present方法不會觸發viewDidLoad方法。
這就是為什么我們會奇怪viewDidLoad方法中的代碼有時執行有時似乎不被執行的原因。其實根源還是在initWithNibName方法的if語句中。
如果你在initWithNibName時,引用了ViewController的view屬性,由于此時view為nil,將觸發nib文件的加載行為,導致viewDidLoad方法不等present就提前調用了。由于initWithNibName方法中ViewController成員還未初始化,導致任何對這些成員的引用都是無效的。
比如在viewDidLoad方法中,由于該方法提前執行,導致數據訪問對象還是nil(initWithNibName仍然未執行完)。如果此時在viewDidLoad方法想通過數據訪問對象獲取表格數據,將得到空。這樣從表面上看,viewDidLoad方法似乎未被執行。
這個情況可以通過兩種方法來改進:
一、initWithNibName方法中,不要有任何成員初始化的代碼。把這些代碼移到viewDidLoad方法開始進行。
二、由于initWithNibName方法保證是在present方法中進行,我們也可以在initWithNibName方法中保留成員初始化代碼。但把原來viewDidLoad方法中的代碼移到appear方法中。也就是,最好不要在viewDidLoad方法中進行和成員初始化無關的事情。這樣還有一個好處,每次presentViewController,都會執行appear方法中的代碼(如果是viewDidLoad方法,則只會在加載nib時執行)。