我们知道,所有的对象都要初始化但是有些对象可能有很多初始化方法(根据初始的信息来选择用哪个).我们以iOS的UI框架UIKit为例,其中有个类叫做UITableViewCell
,初始化该类对象 时,需要指明其样式及标识符,标识符能够区分不同类型的单元格。由于这种对象的创 建成本较高,所以绘制表格时可依照标识符来复用,以提升程序效率。我们把这种可为 对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”(designated initializer)
我们看下面这个NSDate的例子:
-(id)init
-(id)initWithString:(NSString*)string
-(id)initWithTimelntervalSinceNow:(NSTimelnterval)seconds
-(id)initWithTimelnterval:(NSTimelnterval)seconds
sinceDate:(NSDate*)refDate
-(id)initWithTimeIntervalSinceReferenceDate:(NSTimelnterval)seconds
-(id)initWithTimeIntervalSincel970:(NSTimelnterval)seconds
那么多的初始化方法中,我们要选一个全能初始化方法,让其他的初始化方法都来调用它。,只有在全能初始化方法中,才会存储内部数据。这样的话,当底层数据存储机制改变时,只需修改此方法的代码就好,无须改动其他初始化方法。
我们来用代码举例:
首先定义一个表示矩形的类:
#import <Foundation/Foundation.h>
@interface EOCRectangle : NSObject
@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;
@end
然后定义一个初始化方法:
-(id) initwithwidth: (float) width andHeight:(float)height
{
if ((self = [super init])) {
_width = width;
_height = height;
}
return self;
}
这样就会有一个问题,当有人用[[EOCRectanglealloc]init]
来创建矩形时,因为NSObject
中已经实现了init
方法,如果把alloc
方法分配好的EOCRectangle
交由此方法来初始化,那么矩形的宽度与高度就是0,因为全部实例变量都设为0了。这种情况我们应该覆写init
方法:
// Using default values
-(id)init {
return [self initWithWidth:5.Of andHeight:10.Of];
)
// Throwing an exception
-(id)init {
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:@"Must use initWithWidth:andHeight: instead."
userInfo:nil];
}
还有一种情况,当我们创建名叫EOCSquare
的类,令其成为EOCRectangle
的子类时,新类的初始化方法写的时候要注意:
@import "EOCRectangle.h"
@interface EOCSquare : EOCRectangle
-(id)initWithDimension:(float)dimension;
@end
@implementation EOCSquare
-(id)initWithDimension:(float)dimension {
return [super initwithwidth:dimension andHeightidimension];
}
@end
这了我们发现上面代码的初始化方法调用了父类的初始化方法,这样可能会导致一个问题:创建出一个”高度”和“宽度”不相等的正方形。所以:如果子类的全能初始化方法与超类方法的名称不 同,那么总应覆写超类的全能初始化方法。在EOCSquare
这个例子中,应该像下面这样覆写EOCRectangle
的全能初始化方法:
-(id)initWithWidth:(float)width andHeight:(float)height {
float dimension = MAX (width, height);
return [self initWithDimension:dimension];
}
覆写了这个方法之后,即便使用init
来初始化EOCSquare
对象,也能照常工作。原因在于, EOCRectangle
类覆写了 init
方法,并以默认值为参数,调用了该类的全能初始化方法。在用init
方法初始化EOCSquare
对象时,也会这么调用,不过由于“initWithWidth:andHeight:”
已经在子类中覆写了,所以实际上执行的是EOCSquare
类的这一份实现代码,而此代码又会调用本类的全能初始化方法。因此一切正常,调用者不可能创建出边长不相等的EOCSquare
对象。
当然如果我们不想覆写父类的全能初始化方法,认为这是调用者自己犯了错误。在这种情况下,常用的办法是覆写超类的全能初始化方法并于其中抛出异常:
-(id) initwithwidth: (float) width andHeight: (float) height {
@throw
[NSException
exceptionWithName:NSInternallnconsistencyException
reason: @"Must use initWithDimension: instead."
userInfo:nil];
有时如果某个队形对象的实例有两种完全不同的创建方式,必须分开处理,所以就要编写多个全能初始化方法。只要记住每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上,应该先调用超类的相关方法,然后再执行与本类有关的任务。
要点
- 在类中提供一个全能初始化方法,并于文档里指明。其他初始化方法均应调用此方法。
若全能初始化方法与超类不同,则需覆写超类中的对应方法。
如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。