为什么指定初始化函数不能在其基类中调用次要初始化函数?

9
根据文档,在Objective-C中,一个类的指定初始化方法必须调用其基类的指定初始化方法。另一个规则是,辅助初始化方法必须调用其自身类的指定初始化方法。但如果遵循第二个规则,为什么指定初始化方法不能调用其基类中的辅助初始化方法呢?这个基类的辅助初始化方法最终会调用其自己级别的指定初始化方法,所以对象仍将被正确初始化,对吗?区别似乎在于谁为缺失的变量选择默认值——您还是您的基类。

1
苹果在这里也有一个很好的解释:https://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/Initialization/Initialization.html - lnafziger
2个回答

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”。因此,您忠实地重写了您的子类中指定的初始化方法,但是您调用了父类的一种次要初始化方法:

@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:,...然后反过来又调用了您的指定初始化器覆盖。无限递归。堆栈溢出。您得到了段错误核心转储蓝屏。

这就是为什么您总是要使用超类的指定初始化器。


这可能只会发生在您的init被称为其中一个超类的init的相同名称的情况下。如果您称其为“initWithoutBob:”,那么就没问题了,对吧? - Robert
那就不是指定的初始化程序。 - rob mayoff

3
如果一个指定的初始化器在其自己的类中调用了一个次要初始化器,那么它就不会担任指定初始化器的角色。指定初始化器的目的是让子类实现者知道他/她可以重写的单个初始化器,所有其他初始化器都将通过该初始化器传递。
如果子类中的指定初始化器调用其超类中的非指定初始化器,那么超类初始化器很可能会转而调用另一个初始化器。由于方法分派的动态性质,它无法限制仅调用其级别实现的方法。这个调用很可能是到子类的覆盖之一,这将进入子类的指定初始化器,导致无限递归。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接