views:

34

answers:

5

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?

A: 

Hi Joey,

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.

phunehehe
A: 

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.

KennyTM
+1  A: 

When you create a subclass, you must override 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."

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.

Jeremy W. Sherman
+1  A: 

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++.

Justin
+1  A: 

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.

JeremyP