initWithCoder:&awakeFromNib&initWithFrame:&init&initWithNibName:bundle:&loadNibNamed的作用及比较

很多朋友如果是初学iOS开发,可能会被其中的几个加载方法给搞得晕头转向的,但是这几个方法又是作为iOS程序员必须要我们掌握的方法,下面我将对这几个方法做一下分析和对比,看看能不能增加大家对几个方法的理解和使用.

首先是常用的加载方法有:

  • initWithNibName:bundle: (加载带有XIB的控制器)
  • loadView (控制器的View为空的时候调用,帮控制器加载View)
  • initWithCoder: (是当从nib文件中加载对象的时候会调用)
  • awakeFromNib (当.nib文件被加载的时候,会发送一个awakeFromNib的消息到.nib文件中的每个对象)
  • initWithFrame: (代码创建View时调用,是懒加载,只有到需要显示时,子控件才不是 nil)
  • init(代码使用创建控件alloc init 时,系统底层调用init方法)

首先我们开始从控制器(viewController)的加载开始说起:
  我们加载控制器可以使用代码也可以使用storyboard

----NO1. 对于使用代码加载控制器(这里是创建控制器哦,不是创建UIView,关于创建UIView另外再谈),这里的ViewController带有一个xib(storyboard,系统默认加载的,相当于显示的是控制器的view)

ViewControllerWith *vc = [[ViewControllerWith alloc] init];

在这个加载过程中,相关方法调用顺序是:

  • init
  • initWithNibName:bundle: - 加载带有xib的控制器(默认ViewController)
  • loadView - 加载控制器视图
  • viewDidLoad - 加载完毕

----NO2. 上面讲了加载storyboard中的控制器,我们现在加载自定义控制器并且带有xib的情况(也就是当我们Command + N,勾选also create XIB file的情况)

注意哦:看看后缀名,第一种情况是加载在storyboard中的控制器(默认是main.storyboard),现在讲的是后缀.xib的控制器,两个概念哈 - 我们系统默认加载是main.storyboard中的xib,区别是里面都是控制器,可以设置箭头

图1
图2
图3

看看上面的加载控制器的图片,加载控制器可以设置箭头的哦,箭头的设置表示默认加载箭头指向的的控制器,但是我们现在讲的这种情况是加载自定义控制器的xib的情况(这种xib其实是UIVIew表示使用xib中的View去代替控制器的view来显示,看图):


图4
图5

在这里图1-3和图4-5一个是storyboard中一个是xib中,前者是控制器,后者本质是UIView,使用xib表示UIView去显示控制器的view,两者之间有区别的哦不要搞错了

继续说第二种情况,对于第二种情况的控制器加载是:

BackViewController *backVC = [[BackViewController alloc] initWithNibName:@"BackViewController" bundle:nil];

在加载过程中,相关方法调用顺序是:

  • initWithNibName:bundle:
  • loadView
  • viewDidLoad

对于上面的这种情况,我专门做了一个小例子,看代码:
在这里我使用纯代码:就是不使用系统默认加载的main.storyboard中的控制器,自己加载想要加载的控制器(OC程序员必备技能)
首先是去掉main.storyboard中的控制器,去掉viewController:自定义控制器并且加载它

图6
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    JNTestViewController *vc = [[JNTestViewController alloc] initWithNibName:@"jtest" bundle:nil];
    self.window.rootViewController = vc;
    [self.window makeKeyAndVisible];
    return YES;
}

在这里我自定义控制器,并且将之设置为window的根控制器,当然我使用(initWithNibName)去加载我指定文件(控制器和view)
注意:这里关于xib的加载,我们要知道,我在这里是将xib文件命名为jtest,但是一般来说,创建自定义控制器的时候xib的命名是和自定义控制器是同名的,如果xib和自定义控制器同名,那么此时我们就可以直接init创建不需要指定xib的名字:

JNTestViewController *vc = [[JNTestViewController alloc] init];

可以这样做的原因是,系统在底层首先调用init方法,在init方法内部自动会调用(initWithNibName)方法,首先系统先看看是否有指定名字的xib,如果没有就加载控制器同名但是去掉Controller的xib,还没有就加载与控制器同名的xib

----NO3. storyboard中加载控制器使用的相关方法
前面说了,在后缀名是storyboard中加载的是控制器,可以设置箭头来指定默认想要系统去加载的控制器,那么如果使用代码区加载想要加载的控制器呢,看代码:
首先在Main.storyboard中去掉viewController的箭头,添加id,如图:

图7

在指定了id之后,根据id加载指定的控制器,看代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    //1.创建窗口
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
     UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];//nil表示从mainBundle中取资源
    ViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"jntest2"];
    self.window.rootViewController = vc;
    [self.window makeKeyAndVisible];
    return YES;
}

依旧是使用纯代码,这种做法首先是指定想要加载的storyboard中的控制器的id,然后根据id加载指定的控制器,关键是(instantiateViewControllerWithIdentifier)方法的使用
在这个加载过程中 相关方法调用顺序是:

  • initWithCoder:
  • awakeFromNib
  • loadView(控制器的View的子控件也是在控制器实例化之后加载view)
  • viewDidLoad

OKay,在上面讲了关于加载控制器,下面呢我们将要讲讲关于加载UIView调用的方法
----NO1.首先是纯代码加载:
在这个加载过程中 相关方法调用顺序是:

  • init
  • initWithFrame:
  • 是init调用了initWithFrame:

----NO2. xib的方式加载UIView
实现自定义xib - MyView.xib,如下图:

图8

在控制器中加载自定义的xib,将xib显示在控制器的view上,看代码:

- (void)viewDidLoad {
    UIView *myViews = [[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil] lastObject];
     myViews.frame = CGRectMake(10, 10, 100, 100);
    [self.view addSubview:myViews];
}

在对控制器和view的加载有所了解之后,我们接着来谈论几个方法之间两两的区别

NO1. init和initWithFrame方法
首先当代码创建控件时,会有init,此时会底层调用init方法,但是init又会在内部调用initWithFrame方法,总的来说,两个方法中作用都是对控件进行创建,在实际开发中可以将控件的创建直接写在initWithFrame方法即可

NO2. initWithFrame和initWithCoder方法
我们在创建UIVIew的时候,一般会使用两种方式:一种是代码,一种是拖控件(interface builder也就是使用nib文件的方式),我们时候拖控件的方式此时initWithFrame方法不会被调用,因为nib文件知道如何初始化该view(拖控件的时候已经定义好了长度高等属性),使用拖控件的方式会调用initWithCoder方法,在该方法中可以重新定义我们在nib中已经设置的各项属性
在使用代码进行view的创建的时候需要注意:当我们创建UIView的子类的时候,我们使用initWithFrame方法实例化UIVIew,并且特别注意:如果在子类中重载initWithFrame方法,必须先调用父类的initWithFrame方法,否则会出现一些意想不到的问题,看看使用initWithFrame创建的一般代码格式:

JNView.m:
#import "JNView.h"
@implementation JNView
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
   
    //在该方法中进行初始化设置
    
    return self;
}
@end

简单点:initWithoder 是当从nib文件中加载对象的时候会调用;initWithFrame是初始化并返回一个新的视图对象,在使用代码创建对象的时候调用.由此:当我们在自定义控件的时候(使用代码创建),一般的套路都是先在init或者initWithFrame方法中做子控件的创建和初始化,在layoutSubviews方法中进行子控件的布局,然后再重写子控件的setter方法给子控件设置数据

NO3. initWithNibName 和 loadNibNamed 方法
我的理解是,使用initWithNibName时加载的是控制器,使用loadNibNamed时加载的是控件比如UIView,UIButton,并且由于在一个xib中可以有多个控件,所以该方法返回的是数组

NO4. initWithCoder:和awakeFromNib方法

  • initWithCoder: 只要对象是从文件(xib或storyboard)解析来的,就会调用
  • awakeFromNib 从xib或者storyboard加载完毕就会调用
  • 同时存在会先调用initWithCoder:

那么关于initWithCoder和awakeFromNib的关系:
首先调用initWithCoder加载xib或者storyboard,然后方法内部发送awakeFromNib消息给每个xib中的对象将对象唤醒,也就是说如果xib中手动拖拽了一个UIView在initWithCoder方法中该UIView是处于未被唤醒状态,此时在initWithCoder方法中去手动向UIView的子控件添加控件,手动添加的控件不会被显示
简单点:就是说在initWithCoder方法中添加子控件是可以显示,但是添加子控件的子控件不被显示,再简单点的话就是说以后想要手动添加控件在xib中请直接写在awakeFromNib方法中

可能还有些朋友对这两个方法的区别还是有点晕,没关系,我做了一个例子,一起看看以上两个方法在具体使用上的区别:

我自定义UIView并且关联xib,看下图:

图9

提供一个类工厂方法快速创建该自定义UIView

  + (instancetype)uiview
{
    return [[[NSBundle mainBundle] loadNibNamed:@"JNUIView" owner:nil options:nil] lastObject];
}

匿名分类中拿到图中拖拽的UIView,并且定义了两个UILabel

@interface JNUIView()
@property (weak, nonatomic) IBOutlet UIView *cyView;
@property(weak, nonatomic) UILabel *JNLabel;
@property(weak, nonatomic) UILabel *JNLabel1;
@end

我先在initWithCoder中创建一个子控件(手动的哦),并且该控件是添加在当前的xib自身中不是子控件上

//创建子控件
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        UILabel *label = [[UILabel alloc] init];
        label.backgroundColor = [UIColor blueColor];
        label.text = @"JN";
        [self addSubview:label];
        self.JNLabel = label;
        //-----------
        
    }
    return self;
}

结果如下:

图10

我向子控件cyView中添加控件,看看结果

  //创建子控件
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        UILabel *label = [[UILabel alloc] init];
        label.backgroundColor = [UIColor blueColor];
        label.text = @"JN";
        [self addSubview:label];
        self.JNLabel = label;
        //-----------
        UILabel *label1 = [[UILabel alloc] init];
        label1.backgroundColor = [UIColor yellowColor];
        label1.text = @"yellow";
        [self.cyView addSubview:label1];
        self.JNLabel1 = label1;
    }
    return self;
}
图11

上面的结果显示向子控件cyVIew中添加子控件是不行的,那么如果我非要向cyView中添加子控件该怎么办? - 利用awakeFromNib,看代码:

  -(void)awakeFromNib
{
    [super awakeFromNib];
    UILabel *label1 = [[UILabel alloc] init];
    label1.backgroundColor = [UIColor yellowColor];
    label1.text = @"yellow";
    [self.cyView addSubview:label1];
    self.JNLabel1 = label1;

}

结果如何,看下图:

图12

结果已经显示了,利用awakeFromNib可以实现向xib的子控件中添加子控件,因为此时子控件已经被唤醒可以添加子控件的子控件的,总之记住:创建控件写在awakeFromNib方法中就可以了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容