根据文档,Objective-C中类的指定初始值设定项必须调用其基类的指定初始值设定项。
另一个规则是辅助初始值设定项必须调用自己类的指定初始值设定项。
但如果遵循第二条规则,为什么指定的初始化程序不能在其基类中调用辅助初始化程序?这个基础辅助初始化程序最终将调用它自己的级别D.I.,因此该对象仍将被正确初始化,对吧?
差异似乎是谁选择缺失变量的默认值 - 您或您的基类。
答案 0 :(得分:9)
让我们考虑一下NSSet
。它有一个指定的初始化程序:
- (id)initWithObjects:(const id *)objects count:(NSUInteger)cnt {
// initialization code here
return self;
}
它还有一些辅助初始化器,如下所示:
- (id)initWithArray:(NSArray *)array {
NSUInteger count = array.count;
id objects[count];
[array getObjects:objects range:NSMakeRange(0, count)];
return [self initWithObjects:objects count:count];
}
现在你想要一个NSSet
的子类自动拒绝字符串“Bob”。因此,您尽职尽责地覆盖子类中的指定初始化程序,但是您调用了super的辅助初始化程序之一:
@implementation BobRejectingSet
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count {
NSMutableArray *array = [[NSMutableArray alloc] initWithCount:count];
for (NSUInteger i = 0; i < count; ++i) {
if (![objects[i] isEqual:@"Bob"]) {
[array addObject:objects[i]];
}
}
return [super initWithArray:array];
}
执行此操作时会发生什么:
BobRejectingSet *noBobs = [[BobRejectingSet alloc] initWithArray:someObjects];
由于您未覆盖initWithArray:
,程序会调用-[NSSet initWithArray:]
,它会调用指定的初始化程序initWithObjects:count:
。您覆盖指定的初始化程序,因此它会调用您的方法。你的方法过滤掉了Bobs,然后调用了超级的辅助初始化程序initWithArray:
...它转过来并再次调用指定的初始化程序覆盖。无限递归。堆栈溢出。你会得到分段违规 - 核心转储的蓝调。
这就是为什么你总是使用super的指定初始化器。
答案 1 :(得分:3)
如果指定的初始化程序要在其自己的类中调用辅助初始化程序,则它将不会担任指定初始化程序的角色。指定初始化程序的要点是,子类实现者知道单个初始化程序可以覆盖所有其他初始化程序将通过的漏斗。
如果子类中的指定初始化程序要在其超类中调用非指定的初始化程序,则超类初始化程序可能会转向并调用另一个初始化程序。由于方法调度的动态特性,它不能仅限于调用在其级别实现的方法。该调用很可能是子类的一个覆盖,它将汇入子类的指定初始化器,导致无限递归。