2

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

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

4

5 回答 5

3

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

如果-initin super 实现为

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

然后调用图将循环:

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

一如既往self地使用当前类(“子类”)。


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

不,这不是必需的。首选是调用super's 最专业的初始化程序,因此 super 没有机会-initXXX回调子类的-initYYY.

于 2010-10-23T07:21:18.890 回答
3

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

作为初始化子类的一部分,您必须调用超类的指定初始化程序之一。

一个类的文档应该指定它的指定初始化器。如果不是,则通常假定指定的初始化程序是超类提供的最具体的初始化程序(接受最多参数的初始化程序)。

有关详细信息,请参阅“Objective-C 编程语言:分配和初始化对象”。 [注意:截至 2013 年 12 月,此内容似乎不再可通过 Apple 文档中心获得。以前的语言参考已被更多面向任务的教程和概念文档所取代。]

至于你的具体问题:

为什么是这样?这样超类就有机会初始化它的状态。然后,您可以继续初始化您添加的超出超类提供的状态。

为什么从子类的 initWithFrame 调用 super 的 init 会导致无限循环?因为 for不是指定的初始化器,尽管它是NSView' s。因此覆盖它以调用其指定的初始化程序,. 如果您从您的呼叫,您现在有呼叫呼叫呼叫呼叫…</p> -initNSObjectNSView-initWithFrame:-init-initWithFrame:-initWithFrame:-init-initWithFrame:-init:

这是否意味着…?不,因为这不是必需的。您必须了解实际文档,而不是道听途说。

于 2010-10-23T08:26:05.377 回答
3

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

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

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

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

于 2010-10-23T10:05:35.047 回答
1

从 C++ 的角度来看:

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

这不是真的。这只是常见的。您可以自由调用任何记录为有效初始化程序的超类初始化程序。

像这样查看它可能会有所帮助:查看超类的初始化程序并确定哪些是受支持的。

  • 有时有一个指定的初始化器
  • 有时会有新的初始化程序(例如,可能会向超超类添加参数的初始化程序)
  • 有时有从超超类继承的初始化程序

对于指定的初始化器:认为它受保护

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

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

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

这就是调用不应调用的初始化程序的效果(未定义的行为)。

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

只要您通过支持的超类初始值设定项之一进行调用,就可以了。

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

objc 不支持初始化程序的隐藏/可见性。一旦它在超类的接口中,它就在那里(并且您可以在编译器无法帮助您的情况下做出错误的选择) - 您应该确定初始化程序的可见性图并相应地编写您的子类。objc 缺少您习惯于在 c++ 中具有的语言功能。

于 2010-10-23T08:51:10.167 回答
0

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

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

于 2010-10-23T07:20:37.957 回答