iOS提供指定构造器

提供指定构造器(Designated initializer)

如果自己编写框架或者查看开源框架的一些初始化接口,或多或少都会发现,一般框架都会提供非常丰富便利的初始化接口,这些初始化接口的背后都会集中调用某个核心的初始化方法。 iOS的UIKit中的类,大多都会有一个核心的初始化方法或者称为指定构造器,比如UIView- (instancetype)initWithFrame:(CGRect)frame,UIViewController- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil 这样做的好处是,可以使类的初始化方法看起来简单明了,减少代码量,方便维护初始化接口,当类发生一些改变的时候,只需要更改核心初始化方法

另外提供指定的构造器可以为属性或者实例变量提供默认值,让类在初始化的时候每个属性都有合适的初始值,不至于是nil或者0,这样可以避免一些错误,更加安全。比如如果提供了一个数组属性,代码写了很多,最后调试的时候不知道哪里出错,最后发现数组没有初始化或懒加载。

Object-C

《Effective Object-C 2.0》 第16条 就有建议提供指定初始化方法;相对于Swift的构造语法,Object-C初始化语法有点宽松,同时也带来很多问题,拿《Effective Object-C 2.0》上的例子来说:

// EOCRectangle.h
@interface EOCRectangle : NSObject

@property (nonatomic, assign, readonly) float width;

@property (nonatomic, assign, readonly) float height;

@end

// EOCRectangle.m
- (instancetype)initWithWidth: (float)width andHeight: (float)height
{
    if (self = [super init]) {
        _width = width;
        _height = height;
    }
    return self;
}
复制代码

1.由于Object-C会继承父类的初始化方法,所有继承NSObject的类都有init的这个方法,为了防止开发者没有使用指定初始化方法还必须要重写这个方法,UIKit中大多数类也重写了这个方法,拿上面的UIViewUIViewController来说,如果直接使用init方法创建实例,底层还是会调用对应的指定初始化方法,拿上面的例子来说,如果要想使类变得完善,还需要处理继承父类的init方法;《Effective Object-C 2.0》中提供了两种方式来处理,个人觉得这两种方式都不能说优雅

- (instancetype)init
{
    return [self initWithWidth: 5.0f andHeight:10.0f];
}
复制代码

如果不进行说明,还要查看源代码才知道默认值,这种方式如果指定初始化方法做出修改,类比较简单还好控制,初始化方法比较复杂就会让事情变得复杂

- (instancetype)init
{
    @throw [NSException exceptionWithName:NSInternalInconsistencyException 
    reason:@"Must use initWithWidth: width andHeight: height" 
    userInfo:nil];
}
复制代码

这种方式就更加是无奈之举,虽然安全性是有了保障,但是这是根本上违背了异常设计的初衷,我很难把调用这个方法想象成致命的错误

2.如果这时候再来一个继承类的话,由于初始化方法的继承又会使事情变得糟糕,详情可以参考《Effective Object-C 2.0》第16条

总结:

1.尽可能的为类提供指定初始化方法,在初始化方法中为属性提供合适的初始值

2.写好指定初始化方法之后,处理继承父类的初始化方法,如果父类的初始化方法不适用与子类,重写父类的初始化方法并抛出异常。

Swift

Swift构造器的规则在这方面就显得非常现代化和安全,对于上面的问题,swift都有明确的规则进行限定(虽然swift的构造过程规则较多,理解之后使得构造逻辑非常清晰也变得简单)

  1. 明确了指定构造器和便利构造器而且便利构造器还有关键字标明,甚至明确便利构造器的调用链的最后必须调用到同一类的指定构造器
  2. 类中的所有存储型属性必须在构造过程中设置初始值,Swift的两段式构造过程还提供了构造过程的安全检查,使代码更加安全
  3. swift中的子类不会默认继承父类的构造器(满足一定条件继承父类的构造器,实际上还是为新引入的存储属性提供了默认的初始值,这样也简化了构造器的编写)

对于上面的例子Swift就变得简单的多

class Rectangle: NSObject {
    
    let width: Float
    let height: Float
    
    init(width: Float,height: Float) {
        self.width = width
        self.height = height
    }
}

class Square: Rectangle {
    
    let dimension: Float
    
    init(dimension: Float) {
        self.dimension = dimension
        super.init(width: dimension, height: dimension)
    }
    
}
复制代码

对于Rectangle,它提供了指定构造器也就不会继承NSObject的构造方法,你也只能使用指定构造器来实例化Rectangle

对于Square也一样,不用像Object-C一样来处理继承的父类的构造方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值