编写高质量代码的52个有效方法

52个有效方法(16) - 提供全能初始化方法

2018-09-04  本文已影响7人  SkyMing一C

我们把可为对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”(designated initializer)。

如果类有多个初始化方法,那么需要在其中选定一个作为“全能初始化方法”,令其他初始化方法都来调用它。

于是,只有在全能初始化方法中,才会存储内部数据。这样的话,当底层数据存储机制改变时,只需要修改此方法的代码就好,无需改动其他初始化方法。

全能初始化方法示例
@interface EOCRectangle : NSObject
@property (nonatomic,readonly) float width;
@property (nonatomic,readonly) float height;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
@end

@implementation EOCRectangle
//全能初始化方法
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height
{
    self = [super init];
    if (self) {
        _width = width;
        _height = height;
    }
    return self;
}
@end

其中- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height这个方法就是全能初始化方法。其他的初始化方法都应该调用这个方法来创建对象。

@interface EOCRectangle : NSObject
@property (nonatomic,readonly) float width;
@property (nonatomic,readonly) float height;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
@end
@implementation EOCRectangle
- (instancetype)init{
    return [self initWithWidth:500 height:500];
}
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height
{
    self = [super init];
    if (self) {
        _width = width;
        _height = height;
    }
    return self;
}
@end
@interface EOCSquare : EOCRectangle
- (instancetype)initWithDimension:(CGFloat)dimension;
@end
@implementation EOCSquare
//如果使用者继续使用父类的全能初始化方法呢,这样就有可能出现宽高不等的正方形。所以还应该阻止使用者直接调用父类的全能初始化方法
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height{
    @throw  [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Must be use initWithDimension :instead" userInfo:nil];
}
- (instancetype)initWithDimension:(CGFloat)dimension
{
   return  [super initWithWidth:dimension height:dimension];
}
//如果使用者还是用init来创建,这样还是调用父类中的init方法,还是有可能出现长宽不等的情况,所以还应该复写一下init方法
-(instancetype)init{
    return [self initWithDimension:500];
}
@end
#import <Foundation/Foundation.h>

@interface EOCRectangle : NSObject <NSCoding>
@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;
- (id)initWithWidth:(float)width 
          andHeight:(float)height;
@end

@implementation EOCRectangle

// Designated initialiser
- (id)initWithWidth:(float)width 
          andHeight:(float)height
{
    if ((self = [super init])) {
        _width = width;
        _height = height;
    }
    return self;
}

// Super-class’s designated initialiser
- (id)init {
    return [self initWithWidth:5.0f andHeight:10.0f];
}

// Initialiser from NSCoding
- (id)initWithCoder:(NSCoder*)decoder {
    // Call through to super’s designated initialiser
    if ((self = [super init])) {
        _width = [decoder decodeFloatForKey:@"width"];
        _height = [decoder decodeFloatForKey:@"height"];
    }
}

@end

请注意,NSCoding协议的初始化方法没有调用本类的全能初始化方法,而是调用了超类的相关方法。然而,若超类也实现了NSCoding,则需改为调用超类的"initWithCoder:"初始化方法。例如,在此情况下,EOCSquare类就得这么写:

#import "EOCRectangle.h"

@interface EOCSquare : EOCRectangle
- (id)initWithDimension:(float)dimension;
@end

@implementation EOCSquare

// Designated initialiser
- (id)initWithDimension:(float)dimension {
    return [super initWithWidth:dimension andHeight:dimension];
}

// Super class designated initialiser
- (id)initWithWidth:(float)width andHeight:(float)height {
    float dimension = MAX(width, height);
    return [self initWithDimension:dimension];
}

// NSCoding designated initialiser
- (id)initWithCoder:(NSCoder*)decoder {
    if ((self = [super initWithCoder:decoder])) {
        // EOCSquare’s specific initialiser
    }
}

@end

每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上,然后再执行与本类有关的任务。


Designated Initializer 指定初始化方法

NS_DESIGNATED_INITIALIZERNS_UNAVAILABLE

- (id)initWithDimension:(float)dimension NS_DESIGNATED_INITIALIZER;
/**
 :nodoc:
 */
- (instancetype)init NS_UNAVAILABLE;//< 直接标记 init 方法不可用

/**
 :nodoc:
 */
+ (instancetype)new NS_UNAVAILABLE;
要点
  1. 在类中提供一个全能初始化方法,并于文档中指明。其他初始化方法均应调用此方法。

  2. 若全能初始化方法与超类不同,则需覆写超类中的对应方法。

  3. 如果超类中的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。

上一篇下一篇

猜你喜欢

热点阅读