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

发布于 2024-09-28 14:10:15 字数 340 浏览 7 评论 0原文

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

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

I have heard that when you have a subclass, you are supposed to initialize the superclass with the same init function from within the subclass's init. What I mean is that the subclass's init should call [super init] and the subclass's initWithFrame should call [super initWithFrame]. Why is this? Why does calling the super's init from a subclass's initWithFrame result in an infinite loop?

If this is required, then does this mean I can't create a new init function within a subclass such as initWithPoint and have that call super's init or initWithFrame simply because the super class doesn't have initWithPoint? I guess the heart of the question here is simply why it's improper to call a different super class, something that's confusing me possibly because of my c++ background?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

原来是傀儡 2024-10-05 14:10:15

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

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

类的文档应指定其指定的初始值设定项。如果不是,则通常假定指定的初始值设定项是超类提供的最具体的初始值设定项(采用最多参数的初始值设定项)。

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

至于您的具体问题:

这是为什么?以便超类有机会初始化其状态。然后,您可以继续初始化您在超类提供的之上添加的状态。

为什么从子类的 initWithFrame 调用父类的 init 会导致无限循环?因为,对于 NSView 来说,-init 不是指定的初始化器,尽管它是 NSObject 的。因此,NSView 重写它以调用其指定的初始化器 -initWithFrame:。如果您已从 -initWithFrame: 调用了 -init,那么现在 -initWithFrame: 正在调用 -init调用 -initWithFrame: 调用 -init: 调用...

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

When you create a subclass, if you implement an initializer, then you must be sure to call the superclass's designated initializer(s), and you must provide at least one designated initializer of your own, though this can just be your override of the superclass's.

As part of initializing your subclass, you must call one of the superclass's designated initializers.

A class's documentation should nominate its designated initializers. If not, the designated initializer is generally assumed to be the most specific initializer (the one taking the most arguments) provided by the superclass.

For further details, see "The Objective-C Programming Language: Allocating and Initializing Objects." [Note: As of December 2013, this content no longer appears to be available via Apple's doc center. What was a language reference has been replaced by more task-oriented tutorials and conceptual documentation.]

As to your specific questions:

Why is this? So that the superclass has a chance to initialize its state. You can then go ahead and initialize the state you add above and beyond what the superclass provides.

Why does calling the super's init from a subclass's initWithFrame result in an infinite loop? Because, for NSView, -init is not the designated initializer, though it is NSObject's. So NSView overrides it to call its designated initializer, -initWithFrame:. If you've called -init from your -initWithFrame:, you now have -initWithFrame: calling -init calling -initWithFrame: calling -init: calling…

Does this mean…? No, because this is not required. You must understand the actual documentation, not hearsay.

眼眸印温柔 2024-10-05 14:10:15

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

如果 super 中的 -init 实现为,

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

则调用图将循环:

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

因为 self 始终使用当前类(“子类”)。


如果这是必需的,那么这是否意味着我无法在子类(例如 initWithPoint)中创建新的 init 函数,并且仅仅因为超类没有 initWithPoint 就无法调用超级的 init 或 initWithFrame ?

不,这不是必需的。首选方法是调用 super 最专业的初始化程序,这样 super 的 -initXXX 就不可能回调子类的 -initYYY

Why is this? Why does calling the super's init from a subclass's initWithFrame result in an infinite loop?

If the -init in super is implemented as

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

then the call graph will loop around:

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

as self always uses the current class ("subclass").


If this is required, then does this mean I can't create a new init function within a subclass such as initWithPoint and have that call super's init or initWithFrame simply because the super class doesn't have initWithPoint?

No this is not required. What is preferred is to call super's most specialized initializer, so there's no chance super's -initXXX calls back the subclass's -initYYY.

强辩 2024-10-05 14:10:15

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

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

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

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

Why does calling the super's init from a subclass's initWithFrame result in an infinite loop?

Coming from a C++ background as you say you do, the main problem is probably that you are used to the C++ method calling paradigm. In Objective-C, you do not call functions of objects. It's not even technically completely correct to say you invoke methods. In Objective-C, you send messages to objects and the object decides what to do with them. What it normally does is look up a method in its class and invoke that. The consequence of that is that you cannot control which version of a method in a class hierarchy gets invoked by a message. It's always the method belonging to the class of the object you send the message to (except in one case). It's as if C++ had no non virtual functions, not even the constructors.

The one exception to this is when you send a message to super. In that case, the method on your class is bypassed. This can lead to infinite loops as you have found out. The reason is because we don't call functions, we send messages. So if methodA in class SubKlass sends [super methodB] the implementation of methodB in Klass will be invoked. If it then sends [self methodA] self is still an instance of SubKlass, it hasn't magically transformed into an instance of Klass, so methodA in SubKlass will be invoked.

This is why the rules for initialisers seem so convoluted. Only the designated initialiser is guaranteed not to send one of the other initialisers so you can only safely send the designated initialiser to super in your initialiser.

仄言 2024-10-05 14:10:15

从c++的角度来看:

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

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

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

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

指定初始化程序的初始化程序:将其视为受保护

对于新的初始化程序:将其视为受保护

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

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

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

如果这是必需的,那么这是否意味着我无法在子类(例如 initWithPoint)中创建新的 init 函数,并且仅仅因为超类没有 initWithPoint 就无法调用超级的 init 或 initWithFrame ?

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

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

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

from a c++ perspective:

I have heard that when you have a subclass, you are supposed to initialize the superclass with the same init function from within the subclass's init. What I mean is that the subclass's init should call [super init] and the subclass's initWithFrame should call [super initWithFrame].

that's not true. it's merely common. you are free to call any superclass initializer that is documented as a valid initializer.

it may help to view it like this:
look at the superclass' initalizers and determine which are supported.

  • sometimes there is a designated initializer
  • sometimes there are new initializers (e.g., one which may add an argument to the super-superclass)
  • sometimes there are intializers inherited from the super-superclass

for designated initializers: consider it protected

for new initializer: consider it protected

for inherited initializers: typically consider private when the superclass declares new initializers, otherwise protected

Why does calling the super's init from a subclass's initWithFrame result in an infinite loop?

such is the effect (undefined behavior) of calling an intializer which you should not call.

If this is required, then does this mean I can't create a new init function within a subclass such as initWithPoint and have that call super's init or initWithFrame simply because the super class doesn't have initWithPoint?

this is fine as long as you call through one of the supported superclass initializers.

I guess the heart of the question here is simply why it's improper to call a different super class, something that's confusing me possibly because of my c++ background?

objc doesn't support hiding/visibility for initializers. once it's in a superclass' interface, it's there (and you're able to make bad choices where the compiler can't help you) - you're expected to determine the visibility graph for initializers and write your subclass accordingly. objc is lacking language features that you're accustomed to having in c++.

一杯敬自由 2024-10-05 14:10:15

我不知道你从哪里听说的,但据我所知,这不是必需的。您可以选择用任何您想要的东西来初始化您的子类,只要您调用超类的 init 方法。任何 init 方法都可以工作。

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

I don't know where you heard it from, but AFAIK it's not required. You can choose to init your subclass with whatever you want, provided that you call an init method of the superclass. Any init method will work.

However, if you also have the same init function in your superclass, I think the better thing to do is to call that function, then add your own customization. It's not required, it's just good practice to do so, because that init function may provide some initializations and settings that you may forget to add.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文