iOS开发中、如何选择重写初始化方法?

天地之仁,故万物皆屌丝

说在前面的话

很多时候,我们总是需要重写父类的方法达到我们开发需求。那么在iOS开发过程中,会有哪些初始化方法呢?又应该注意些什么问题?比如在一个自定义的UIView子视图中, 如果重写了initWithFrame:方法, 为什么不应该再重写init方法。

可以先下载项目HGInitManager

强调

  • 1、只谈现象,不谈实质原理。
  • 2、重点在乎的是如何选择,而不是对与错。

类方法

+ (instancetype)new;

对于这个方法,具体的原理可以简写如下:

+ (instancetype)new {
    return [[self alloc] init];;
}

在我们的开发中,这里理解完全是没有问题的。但是实质上的实现可以参考 源代码 objc4-723 & NSObject.mm , 在 NSObject.mm 文件中可以找到这个方法的地方:

image.png

callAlloc 函数的实现就在当前的文件:

image.png

实例方法

在介绍实例的初始化方法的时候,我将采取 MVC 的设计模式向大家介绍。

Model 层

Model层一般都叫模型类:特指直接或间接继承NSObject的类。主要是把 UIView 与 UIViewController 区别开,当然UIView 与 UIViewController 也是集成于NSObject。

init

这个方法是所有OC对象类都有的方法,所以所有的对象类都可以重写。重写样例如下:

- (instancetype)init {
    self = [super init];
    
    // TODO: 给当前self做默认设置
    
    return self;
}

回头看看本文的标题<iOS开发中、如何选择重写初始化方法?>。那么问题来了,这个方法对所有的对象类都通用,我们在什么时候重写才显得更高大上呢?我的建议是:仅仅在模型类(Model)中,才去重写。
例子:

- (instancetype)init {
    self = [super init];
    
    // 设置默认年龄是 18
    self.age = 18;
    
    return self;
}

对,你没有看错。仅仅在 Model 中对数据的默认设置的时候,才有必要重写这个方法。

聪明的你会问,在 UIView 与 UIViewController 中就不可以重写这个方法么?请回到我强调的第二点(2、重点在乎的是如何选择,而不是对与错。)。

在这里我想先说一下的是:在 UIView 与 UIViewController 中坚决不要重写 init 方法。在接下来会介绍在 UIView 与 UIViewController 中应该如何选择重写 初始化方法。

View 层

在这里要清楚的一点是,在 View 层有两种方式加载 View :纯代码与XIB(SB)。这里要区分来介绍,因为这两种方式的初始化方法是两套机制。

纯代码

大家应都知道,纯代码创建 View 有两个方法提供选择,分别是:

- (instancetype)init
{
    self = [super init];
    
    // TODO: 给当前视图做默认设置
    
    NSLog(@" init ");
    
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    // TODO: 给当前视图做默认设置
    
    NSLog(@" initWithFrame: ");
    
    return self;
}

那么问题又来了,这两个方法在 View 中,我们应该如何选择呢?两个都要重写,还是只要重写其中一个?带着这个问题,我们创建一个继承于 UIView 的HGInitView。并且将上面的代码copy到 .m 文件中。

我们在其它地方用来年各种功能方式创建:
方式1:

HGInitView* hgView = [[HGInitView alloc] initWithFrame:CGRectZero];

打印日志如下:

initWithFrame:

打印结果 证明: 仅仅触发了 initWithFrame: 方法。这结果其实没有什么可惊讶的,我们再来看看方式2。

方式2:

HGInitView* hgView = [[HGInitView alloc] init];

打印日志如下:

0.png

打印结果 证明: 即触发了 initWithFrame: 方法 又触发了init 方法。问题来了,如果我用方式2来创建视图的话,不久重复了么?

最终结论:

  • 两种方式都能正确的创建试图。
  • 两种方式都会触发initWithFrame: 方法。

所以在这里的建议是:在 UIView 的子类中不要重写 init 方法,重写 initWithFrame: 方法就足够了。

XIB

也有两种方式给当前的试图做初始化。分别是:


- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    
    // TODO: 给当前视图做默认设置
    self.hgLabel.textColor = [UIColor redColor];
    
    NSLog(@"initWithCoder:");
    
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    
    // TODO: 给当前视图做默认设置
    self.hgLabel.textColor = [UIColor redColor];
    
    NSLog(@"awakeFromNib");
    
}

在这里只有一种方式来加载视图,代码如下:

NSArray* views =  [[NSBundle mainBundle] loadNibNamed:@"HGInitView" owner:nil options:nil];
    HGInitView* hgView = views.firstObject;

打印日志如下:

XIB 文件加载视图

同样两个方法都调用了,并且一切都很正常。现在我有目的的将 awakeFromNib 方法注释,看看效果。很惊讶的发现‘self.hgLabel.textColor = [UIColor redColor];’这句代码没有效果,这是为什么呢?请看下图:

解惑

答案就在上图中,当执行 initWithCoder: 方法的时候,当前试图中的子试图还没有值。所以在个方法中给子试图设置默认效果是无效的。这里只能给其它的属性做默认设置。

这里就不用看只打开 awakeFromNib方法的效果了。在这个方法中,什么都有,什么都可以设置了。

这里也提醒大家:initWithCoder: 方法一般是用于资源文件加载用,一般用于 解档 时用到。一般情况要慎用。

综上所属: 在XIB视图加载中,一般重写awakeFromNib方法就可以了。

View 层总结:

  • 纯代码创建试图,重写 initWithFrame: 方法就足够。
  • XIB 加载视图,重写awakeFromNib方法就足够。

如果当前视图可能用于纯代码,也可能用于XIB 加载,一般是在封装控件的时候比较多。那么按照下图中的写,就够了:

View 层通用

Controller 层

同样 Controller 也有两种方式加载。与之相关的方法如下:


- (instancetype)init {
    self = [super init];
    
    // TODO: 默认设置
    
    return self;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
    // TODO: 默认设置
    self.hgLabel.textColor = [UIColor redColor];
    
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // TODO: 默认设置
    self.hgLabel.textColor = [UIColor redColor];
}

在这里,我就不带着大家一一分析了。直接把我的建议分享给大家,同时也是希望大家动手试试。在这里要强调一点的是 viewDidLoad 说成是初始化方法,真的也有点搞笑 。但是希望你先把我的建议看完,再好好的笑一笑。

笑一笑

我的建议是:

  • 1、直接在 viewDidLoad 方法中直接对所有的默认做设置。
  • 2、在initWithNibName: bundle:方法做非视图的默认设置,在viewDidLoad 方法中做与视图有关的默认设置。
  • 言之总而:在init开头的方法中,不要做与视图有关的设置。

感谢看到这里的小伙伴!到这里,本文就算是结束了,有什么问题,欢迎大家讨教与骚扰。

也可以下载项目HGInitManager

以下介绍添加于 2018-09 《两个重要的宏介绍》

NS_DESIGNATED_INITIALIZER: Designed initializer 与 NS_UNAVAILABLE

NS_DESIGNATED_INITIALIZER 是用于指定初始化方法,NS_UNAVAILABLE 则是用禁用其标记的初始化方法。有一个简单的例子:

// .h 文件
#import <Foundation/Foundation.h>

@interface HGObject : NSObject

- (instancetype)initWithName:(NSString*)name;
// NS_DESIGNATED_INITIALIZER: Designed initializer
- (instancetype)initWithName:(NSString*)name age:(NSInteger)age NS_DESIGNATED_INITIALIZER;

// NS_UNAVAILABLE: 禁止调用
- (void)hgPrint NS_UNAVAILABLE;

@end

// .m 文件
#import "HGObject.h"

@interface HGObject () {
    NSString* _name;
    NSInteger _age;
}

@end

@implementation HGObject

- (instancetype)init {
    return [self initWithName:@"CoderHG"];
}

- (instancetype)initWithName:(NSString *)name {
    return [self initWithName:name age:18];
}

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    _name = name.copy;
    _age = age;
    return self;
}

// 打印
- (void)hgPrint {
    NSLog(@"姓名: %@  年龄: %zd", _name, _age);
}

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

推荐阅读更多精彩内容

  • http://www.cocoachina.com/cms/wap.php?action=article&id=1...
    Kevin追梦先生阅读 1,022评论 0 3
  • 1.自定义控件 a.继承某个控件 b.重写initWithFrame方法可以设置一些它的属性 c.在layouts...
    圍繞的城阅读 3,387评论 2 4
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,139评论 30 470
  • 夜空湛蓝 黑色里是我不曾见过的 那种五彩的明亮 看那颗天边最远的星辰 是璀璨的虹 星空深邃 在那灵魂深处 有一种动...
    云吉阅读 257评论 2 0
  • 2017年10月13日,涵德好父母沙龙的主题是打造您的学习型家庭,由谢君教练主讲。 您认为什...
    安静131阅读 480评论 0 0