第16条:提供“全能初始化方法”
1. 全能初始化方法
*** 概念 ***
可为对象提供必要信息以便其能完成工作的初始化方法叫做全能初始化方法。
其余的初始化方法都要调用它,于是,只有在全能初始化方法中,才会存储内部数据。这样的话,当底层数据存储机制改变时,只需修改此方法的代码就好,无须改动其他初始化方法。
2. NSObject类的init方法
当NSObject类的子类实现init方法时,一般有两种方式:
- 自己指定默认的初始值(只适用于简单的待初始化数据)。
- 抛出异常,指明本类实例必须用“全能初始化方法”来初始化。
3. 例子
/* EOCRectangle头文件 */
#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
/* EOCRectangle实现文件 */
#import "EOCRectangle.h"
@implementation EOCRectangle
// 全能初始化方法
- (id)initWithWidth:(float)width andHeight:(float)height{
if (self = [super init]) { // 先调用NSObject类的init方法
_width = width;
_height = height;
}
return self;
}
// 覆写NSObject类的init方法,指定默认的值
- (id)init{
return [self initWithWidth:5.0f andHeight:10.0f];
}
// 对属性解码,存放到实例变量中
- (id)initWithCoder:(NSCoder *)decoder{
if (self = [super init]) { // 先调用NSObject类的init方法
_width = [decoder decodeFloatForKey:@"width"];
_height = [decoder decodeFloatForKey:@"height"];
}
return self;
}
// 编码
- (void)encodeWithCoder:(NSCoder *)coder{
[coder encodeFloat:_width forKey:@"width"];
[coder encodeFloat:_height forKey:@"height"];
}
@end
/* EOCSquare头文件 */
#import "EOCRectangle.h"
@interface EOCSquare : EOCRectangle
- (id)initWithDimension:(float)dimension;
@end
/* EOCSquare实现文件 */
#import "EOCSquare.h"
@implementation EOCSquare
// 子类的全能初始化方法
- (id)initWithDimension:(float)dimension{
// 调用父类的全能初始化方法
return [super initWithWidth:dimension andHeight:dimension];
}
// 覆写父类的全能初始化方法,防止出现长和宽不等的正方形
- (id)initWithWidth:(float)width andHeight:(float)height{
float dimension = MAX(width, height);
return [self initWithDimension:dimension];
}
// 解码
- (id)initWithCoder:(NSCoder *)decoder{
if (self = [super initWithCoder:decoder]) {// 先调用父类的initWithCoder:方法
// 该子类若有新的属性,需要额外解码
}
return self;
}
@end
/* main函数 */
#import <Foundation/Foundation.h>
#import "EOCRectangle.h"
#import "EOCSquare.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
EOCRectangle *rectangle = [[EOCRectangle alloc] init];
NSLog(@"rectangle: width = %.1f, height = %.1f",rectangle.width, rectangle.height);
EOCRectangle *rectangle2 = [[EOCRectangle alloc] initWithWidth:3.2 andHeight:4.6];
NSLog(@"rectangle2: width = %.1f, height = %.1f",rectangle2.width, rectangle2.height);
EOCSquare *square = [[EOCSquare alloc] init];
NSLog(@"square: width = %.1f, height = %.1f",square.width, square.height);
EOCSquare *square2 = [[EOCSquare alloc] initWithDimension:4.5];
NSLog(@"square2: width = %.1f, height = %.1f",square2.width, square2.height);
}
return 0;
}
输出结果:
rectangle: width = 5.0, height = 10.0
rectangle2: width = 3.2, height = 4.6
square: width = 10.0, height = 10.0
square2: width = 4.5, height = 4.5
要点
- 在类中提供一个全能初始化方法,并于文档里指明。其他初始化方法均调用此方法。
- 若全能初始化方法与超类不同,则需覆写超类中的对应方法。
- 如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。