首页 文章

为什么初始化子类需要调用超类的相同init函数?

提问于
浏览
2

我听说当你有一个子类时,你应该从子类的init中用相同的init函数初始化超类 . 我的意思是子类的init应该调用[super init],子类的initWithFrame应该调用[super initWithFrame] . 为什么是这样?为什么从子类的initWithFrame调用super的init导致无限循环?

如果这是必需的,那么这是否意味着我不能在子类中创建一个新的init函数,例如initWithPoint,并且只是因为超类没有initWithPoint而调用super的init或initWithFrame?我想这里问题的核心就是为什么调用不同的超级类是不合适的,这可能因为我的c背景而让我感到困惑?

5 回答

  • 0

    当你创建一个子类时,如果你实现了一个初始化器,那么你必须确保调用超类的指定初始化器,并且你必须至少提供一个你自己的指定初始化器,尽管这可能只是你超越了超类的 .

    作为初始化子类的一部分,您必须调用其中一个超类的指定初始值设定项 .

    课程的文件应该提名其指定的初始化者 . 如果不是,则通常假定指定的初始化器是超类提供的最具体的初始化器(占据最多参数的初始化器) .

    有关详细信息,请参阅"The Objective-C Programming Language: Allocating and Initializing Objects." [注意:截至2013年12月,此内容似乎不再通过Apple的文档中心提供 . 什么是语言参考已被更多面向任务的教程和概念文档所取代 . ]

    至于你的具体问题:

    Why is this? 这样超类就有机会初始化它的状态 . 然后,您可以继续初始化上面添加的状态,超出超类提供的状态 .

    Why does calling the super's init from a subclass's initWithFrame result in an infinite loop? 因为,对于 NSView-init 不是指定的初始化程序,尽管它是 NSObject . 因此 NSView 会覆盖它以调用其指定的初始值设定项 -initWithFrame: . 如果你从你的 -initWithFrame: 调用了 -init ,你现在有 -initWithFrame: 调用 -init 调用 -initWithFrame: 调用 -init: 调用...

    Does this mean…? 不,因为这不是必需的 . 你必须了解实际的文件,而不是道听途说 .

  • 3

    这是为什么?为什么从子类的initWithFrame调用super的init导致无限循环?

    如果 -init 在super中实现为

    -(id)init {
      return [self initWithFrame:CGRectZero];
    }
    

    然后调用图将循环:

    [subclass initWithFrame:]
       |     ^
       v     |
    [super init]
    

    as self 始终使用当前类("subclass") .


    如果这是必需的,那么这是否意味着我不能在子类中创建新的init函数,例如initWithPoint,并且只是因为超类没有initWithPoint而调用super的init或initWithFrame?

    不,这不是必需的 . 首选的是调用 super 's most specialized initializer, so there' s没有机会超级 -initXXX 回调子类的 -initYYY .

  • 1

    为什么从子类的initWithFrame调用super的init导致无限循环?

    正如你所说的,来自C背景,主要问题可能是你习惯了C方法调用范例 . 在Objective-C中,您不调用对象的函数 . 说你调用方法在技术上甚至都不正确 . 在Objective-C中,您将消息发送到对象,并且对象决定如何处理它们 . 它通常做的是在类中查找一个方法并调用它 . 其结果是您无法控制消息调用类层次结构中的哪个方法版本 . 它始终是属于您发送消息的对象类的方法(在一种情况下除外) . 这就好像C没有非虚函数,甚至没有构造函数 .

    一个例外是当你向super发送消息时 . 在这种情况下,绕过您 class 的方法 . 如您所知,这可能导致无限循环 . 原因是因为我们不调用函数,我们发送消息 . 因此,如果类SubKlass中的methodA发送 [super methodB] ,将调用Klass中methodB的实现 . 如果它然后发送 [self methodA] self仍然是SubKlass的一个实例,它没有神奇地转换为Klass的实例,因此将调用SubKlass中的methodA .

    这就是为什么初始化者的规则看起来如此复杂的原因 . 只有指定的初始化程序保证不发送其他一个初始化程序,因此您只能安全地将指定的初始化程序发送到初始化程序中的super .

  • 3

    从c角度来看:

    我听说当你有一个子类时,你应该从子类的init中用相同的init函数初始化超类 . 我的意思是子类的init应该调用[super init],子类的initWithFrame应该调用[super initWithFrame] .

    这不是真的 . 它只是常见的 . 您可以自由调用任何记录为有效初始值设定项的超类初始值设定项 .

    它可能有助于像这样查看它:查看超类'初始化器并确定支持哪些 .

    • 有时会有一个指定的初始化程序

    • 有时会有新的初始值设定项(例如,可能会为超级超类添加一个参数)

    • 有时会有超级超类继承的初始化者

    对于指定的初始化程序:考虑它受保护

    对于新的初始化程序:认为它受到保护

    对于继承的初始化程序:通常在超类声明新的初始化程序时考虑私有,否则受保护

    为什么从子类的initWithFrame调用super的init导致无限循环?

    这是调用你不应该调用的初始化器的效果(未定义的行为) .

    如果这是必需的,那么这是否意味着我不能在子类中创建新的init函数,例如initWithPoint,并且只是因为超类没有initWithPoint而调用super的init或initWithFrame?

    只要您通过其中一个支持的超类初始化程序调用,这就没问题了 .

    我想这里问题的核心就是为什么调用一个不同的超级类是不合适的,这可能因为我的c背景而让我感到困惑?

    objc不支持初始化程序的隐藏/可见性 . 一旦它在超类的界面中,它就在那里(并且你能够在编译器无法帮助你的地方做出错误的选择) - 你应该确定初始化器的可见性图并相应地编写你的子类 . objc缺乏你习惯于在c中使用的语言功能 .

  • 3

    我不知道你从哪里听到它,但AFAIK不是必需的 . 您可以选择使用您想要的任何内容来初始化子类,只要您调用超类的init方法即可 . 任何init方法都可以 .

    但是,如果你的超类中也有相同的init函数,我认为更好的做法是调用该函数,然后添加自己的自定义 . 这不是必需的,这是一个很好的做法,因为init函数可能会提供一些您可能忘记添加的初始化和设置 .

相关问题