在子类中覆盖init方法

44

在Objective-C中,为了添加自定义初始化逻辑,是否需要覆盖子类的所有继承构造函数?

例如,对于一个具有自定义初始化逻辑的UIView子类,以下方法是否正确?

@implementation CustomUIView

- (id)init {
    self = [super init];
    if (self) {
        [self initHelper];
    }
    return self;
}

- (id)initWithFrame:(CGRect)theFrame {
    self = [super initWithFrame:theFrame];
    if (self) {
        [self initHelper];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)decoder {
    self = [super initWithCoder:decoder];
    if (self) {
        [self initHelper];
    }
    return self;
}

- (void) initHelper {
    // Custom initialization
}

@end
3个回答

46

每个Cocoa Touch(和Cocoa)类都有一个指定的初始化方法;对于UIView,如此文档中所述,该方法为initWithFrame:。在这种情况下,您只需要覆盖initWithFrame方法;所有其他调用将级联下去并最终命中此方法。

这超出了问题的范围,但是如果您最终创建具有额外参数的自定义初始化程序,则应确保在分配self时为超类使用指定的初始化程序,如下所示:

- (id)initWithFrame:(CGRect)theFrame puzzle:(Puzzle *)thePuzzle title:(NSString *)theTitle {
    self = [super initWithFrame:theFrame];
    if (self) {
        [self setPuzzle:thePuzzle];
        [self setTitle:theTitle];
        [self initHelper];
    }
    return self;
}

1
那么,即使我使用普通的init实例化CustomUIView,它也会调用initWithFrame吗? - hpique
33
需要记住的重点是,如果从 NIB 实例化一个对象,则会调用 initWithCoder: 方法,而不是 initWithFrame: 方法。 - Pascal
3
@Pascal 提出的是一个很好的观点,可能应该被编辑到答案中。从答案引用的文档关于 initWithFrame 的内容:"如果你使用 Interface Builder 设计界面,当你的视图对象从 nib 文件中加载时,不会调用此方法。" - pauloya
4
根据@Pascal的评论,Sam的回答是错误的。由于无法依赖构造函数的约定,可能更省时间的做法是假设您必须重写所有构造函数,以确保每次运行时都会运行init。 - gdbj
2
@GregoryJohnson 不一定是所有的; initWithCoder: 是特殊的,它是 NSCoding 类别的一部分。在这种情况下,您可以放心地覆盖指定的初始化程序initWithFrame:加上 initWithCoder: - Pascal
显示剩余2条评论

6

如果使用Interface Builder,则所调用的接口为:

- (id)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
       //do sth
    }
    return self;
}

4
一般情况下,您应该遵循指定初始化程序约定。指定的初始化程序是init,它涵盖了所有实例变量的初始化。指定的初始化程序也是类的其他init方法调用的方法。
关于指定初始化程序,苹果的文档可以在此查看
NSView类的指定初始化程序是initWithFrame:。苹果的Cocoa文档总是明确提到一个类的指定初始化程序。
有关initWithCoder:的讨论可以在此处查看

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