作者:Love@YR
鏈接:http://blog.csdn.net/jingqiu880905/article/details/51745617
請尊重原創,謝謝
經常會看到initWithNibName,initWithCoder,awakeFromNib,loadNibNamed 這些方法,各種頭大,網上資料不少,能說清楚的不多,於是今天寫了個demo來測試下這些方法何時調用。
首先,我們從視覺上區分一下loadNibName和initWithNibName。
initWithNibName 是由UIViewController及其子類調用
如:
AViewController *avc = [[AViewController alloc]initWithNibName:@"AViewController" bundle:nil];
而loadNibName則是這樣:
NSArray *objectsInNib = [[NSBundle mainBundle] loadNibNamed:@"BView" owner:nil options:nil];
for(id item in objectsInNib){
if([item isKindOfClass:[yourview class]]){
//找到了你需要的對象,do what you want
}
在這個時候生成後會讓引用計數器變爲1,但是需要注意得owner:self ,在owner自己dealloc得時候,系統會自動幫你將你得youview得引用計數器-1。
在xib裏,第一種(一般我們創建 viewcontroller時勾選了nib生成的)
通常是這樣的:
注意這裏的file’s owner是你的view controller類
底下這個view就是這個view controller的根view
第二種通常是這樣的:
注意看,這裏的file’s owner是NSObject
這裏我拖了3個控件,其中view這個所屬的類名爲BView,同樣其它控件我也需要給它創建類並連接。
默認uiview佔滿屏,改下其size爲freeform即可改變其大小。
一般來說我們調用loadNibNamed的到的數組即這個xib上的所有控件,我們是想要拿一些控件如view,cell等而不是viewcontroller。(這也是最常用的用途)
但是,這裏我們同樣可以拖入viewcontroller, navigationcontroller等任意對象。
如圖:
那麼這裏我們得到的數組裏就能有viewcontroller這個對象了。
那麼同樣兩種創建viewcontroller的方法,分別調用了什麼呢?
測試結果顯示用initWithNibName方法,會調用此CustomViewController的initWithNibName,viewDidLoad
而用loadNibNamed時,執行到此句時,就會調用此數組裏所有對象的initWithCoder,awakeFromNib方法。
比如我在一個xib里加入了一個view,兩個view controller。執行的log顯示當調用loadNibNamed時打印了:
BView——-initWithCoder———-called
AVC——-initWithCoder———-called
bVC——-initWithCoder———-called
BView——-awakeFromNib———-called
AVC——-awakeFromNib———-called
bVC——-awakeFromNib———-called
然後我拿出aVC對象,present,並沒有執行AVC的viewDidLoad。
這個問題 http://www.cnblogs.com/iOS-mt/p/4362335.html 這篇文章也說了,後續再研究。
這下清晰多了。
然後 http://blog.csdn.net/richard_rufeng/article/details/26508977 這篇文章說initWithNibName方法是延遲加載 loadNibNamed方法是即時加載,具體原理先不管了。就這樣。
——————————我是分割線———————————————-
上面說了loadNibNamed方法一般用來加載控件對象的。下面我們來看用此方法生成一個CustomView對象和直接alloc一個此對象調用的方法有何區別:
在某個controller裏我創建了avc並present
AViewController *avc = [[AViewController alloc] init];
[self presentViewController:avc animated:YES completion:nil];
在AViewController的viewDidLoad裏:
NSArray *bvarr = [[NSBundle mainBundle] loadNibNamed:@"BView" owner:nil options:nil];
BView *bv = [bvarr objectAtIndex:0];
// [bv setFrame: CGRectMake(10, 10, 100, 100)];//此句話無效
[self.view addSubview:bv];
這裏BView.xib裏只有一個UIView的子類BView。和上面AVC一樣,loadNibNamed時會依次調用BView的initWithCoder,awakeFromNib。還有view controller所沒有的layoutSubviews。
然後測試發現
1. 對於bview的子控件,都得在awakeFromNib或者layoutSubviews裏設置其屬性才能生效,在initWithCoder是不行的。這篇文章也說了。
2. self.frame必須在layoutSubviews裏設置才能生效!!
3. initWithCoder,awakeFromNib裏設置顯示的效果都不準確。網上有人說是因爲還沒加載完。且如上段代碼所註釋,直接[bv setFrame: CGRectMake(10, 10, 100, 100)]也沒有用。
代碼:
BView.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
NSLog(@"BView-------initWithCoder----------called");
}
return self;
}
-(void)awakeFromNib
{
NSLog(@"BView-------awakeFromNib----------called");
self.bviewLabel.text = @"b商品";
self.bviewLabel.backgroundColor = [UIColor purpleColor];
self.bviewLabel.frame = CGRectMake(5, 10, 50, 30);//當不用autolayout的時候代碼設置self.bviewLabel.frame是生效的
self.backgroundColor = [UIColor greenColor];
// self.frame = CGRectMake(10, 10, 300, 200);
}
-(void)layoutSubviews
{
NSLog(@"BView-------layoutSubviews----------called");
self.frame = CGRectMake(10, 10, 300, 200);//此句必須寫這個方法裏
}
——————————我是分割線———————————————-
下面看看直接alloc生成此view對象會調用哪些方法:
當不在外設置view的frame時,也不在initWithFrame,init裏設置frame時:
CView *cv = [[CView alloc]init];
[self.view addSubview:cv];
打印顯示先後走了initWithFrame,init方法
當在initWithFrame或者init裏或者外部cv setFrame時,會觸發layoutSubviews。
打印顯示先後走了initWithFrame,init,layoutSubviews方法
init裏的setFrame會覆蓋initWithFrame裏的,layoutSubviews裏如果寫了setFrame則會覆蓋init裏的。
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
NSLog(@"CView-------initWithFrame----------called");
self.frame = CGRectMake(0, 0, 40, 40);
self.backgroundColor = [UIColor greenColor];
UILabel * clableview = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 10, 10)];
clableview.text = @"c商品";
clableview.backgroundColor = [UIColor purpleColor];
[self addSubview:clableview];
}
return self;
}
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"CView-------init----------called");
// self.frame = CGRectMake(10, 10, 20, 80);
}
return self;
}
-(void)layoutSubviews
{
NSLog(@"CView-------layoutSubviews----------called");
// self.frame = CGRectMake(10, 10, 80, 20);
}
當使用 CView *cv = [[CView alloc]initWithFrame:CGRectMake(10, 10, 10, 10)];
會調用initWithFrame和layoutSubviews不會調用init方法。
所以可以直接在initWithFrame裏面去設置frame backgroundcolor subview等。