作者: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等。