NSWindowController initialization warning - objective-c

I am developing a document-based app. I am using a dedicated window controller for the document window, and calling the -[NSDocument makeWindowControllers] method.
My window controller is initialized like this:
- (instancetype) init
{
if (self = [super initWithWindowNibName:#"Document" owner:self]) {
}
return self;
}
Here, #"Document" is the .xib file containing the main document window that was created with the project.
The rationale here is that, this window controller is always initialized with this type of window, so the parameter is both hard-coded and hidden away inside the implementation of -init (while also conveniently setting the window owner to self).
So, the side that instantiates the window controller (in my case, the document class) doesn't need to worry about which nib to use and can just call -init.
The problem is, I am breaking the designated initializer chain and Xcode complains with these warnings:
Semantic Issue Method override for the designated initializer of the
superclass '-initWithCoder:' not found.
Semantic Issue Designated initializer missing a 'super' call to a
designated initializer of the super class.
Semantic Issue Method override for the designated initializer of the
superclass '-initWithWindow:' not found.
Semantic Issue Designated initializer invoked a non-designated
initializer.
I would switch the call to -initWithWindowNibName:owner: with one to -initWithWindow: (and after that, set the owner manually, I guess?); but I don't know how to create an NSWindow directly from a nib (or if this is the right thing to do).
EDIT: I just discovered that the warnings are being triggered only because I labeled -init as NS_DESIGNATED_INITIALIZER in my interface. I can remove that label and the warnings go away, but -init is my de-facto designated initializer so I would rather keep it.

Related

Objective C: init and awakeFromNib

I've recently studied some Cocoa based open source projects. I saw that a lot of programs have all initializing code in awakeFromNib and rarely use the designated initializer. I am used to do it that way:
in the overridden designated initializer: do all non-Nib stuff
in awakeFromNib: do all Nib-related stuff
Is this approach wrong?
Yes, it's correct, all nib-related stuff should be done in awakeFromNib method. At the moment when it's called you already have initialized and loaded view from nib, so you already may configure and use it.
As concerned to initializers, please, check this question: iOS: UIView subclass init or initWithFrame:?
The designated initializer is the one that all the other initializers must call. UIView and subclasses are a little unusual in that they've actually got two such initializers: -initWithFrame: and -initWithCoder:, depending on how the view is created. You should override -initWithFrame: if you're instantiating the view in code, and -initWithCoder: if you're loading it from a nib. Or, you could put your code in third method and override both those initializers such that they call your third method. In fact, that's often the recommended strategy.

awakeFromNib() and windowDidLoad()

Can anyone explain what awakeFromNib(), windowDidLoad(), init() does?
I am using a class which is inherited from NSWindowController, i found the precedence as
-init(), -awakeFromNib(), -windowDidLoad().
I want to know what these methods exactly perform.
init is the first method that gets called. This initializes self and all the ivars, properties etc.
awakeFromNib is called after init. When a nib is loaded, the nib loader allocates and initializes all objects, then hooks up all of their outlets and actions. Because of the order in which this happens, you cannot access outlets in your initializer. You can try, but they will all be nil.
After all outlets and actions are connected, the nib loader sends awakeFromNib to every object in the nib. This is where you can access outlets to set up default values or do configuration in code.
windowDidLoad is a delegate method which is called when window is fully loaded. Sent after the window owned by the receiver has been loaded. The default implementation does nothing.

In Objective-C, the rule that designated initializer always get called is not always obeyed?

Can we rely on the fact that in Objective-C, the rule is that a class's designated initializer is always called for sure? Or can we say, it should be almost always true, except a couple of exceptions? For example, for UIView, the docs says:
initWithFrame:
If you create a view object programmatically, this method is the
designated initializer for the UIView class. Subclasses can override
this method to perform any custom initialization but must call super
at the beginning of their implementation.
If you use Interface Builder
to design your interface, this method is not called when your view
objects are subsequently loaded from the nib file. Objects in a nib
file are reconstituted and then initialized using their initWithCoder:
method
Or can we say that, if it is programmatically, the rule should always apply for well designed classes, but Interface Builder is a bit different because it sort of "revive" or build the object from a non-programmatic way. If so, are they other exceptions in general when we do iOS programming?
The fact is that class designed with Interface Builder are unarchived and not initialized .
Being archived involves that the class is not initialized but unarchived, so the initWithCoder: method takes responsibility for setting up the control when it's loaded using the archived attributes configured by Interface Builder.
You should put your initialization operations in the awakeFromNib: method that gets called in each case after the object is loaded, thus you will be sure that your initialization statements will be called .

How is view initialized when loaded via a storyboard?

When view is loaded manually, developer remains in control when it comes to initializations, we choose what initializer to call, what variables to set etc.
When view is loaded from the storyboard segue ... what happens to that initializer? Where should variables be set i'd like to be available once view had been loaded?
Please help me understand the sequence here. How is instance of the class created here, who creates it and how can we intervene and help set it up to our liking?
When a view is loaded from a nib or storyboard, it's -initWithCoder: method is called. Like -initWithFrame:, -initWithCoder: is a designated initializer for UIView. If you're going to do any custom initialization for a UIView subclass, you should make sure that it happens for both these methods. One common technique is to add a common initialization method that you call from both -initWithFrame: and -initWithCoder:. See my answer to Custom view and implementing init method? for a more detailed description.
Note that the documentation for -initWithFrame: explains:
If you use Interface Builder to design your interface, this method is
not called when your view objects are subsequently loaded from the nib
file. Objects in a nib file are reconstituted and then initialized
using their initWithCoder: method, which modifies the attributes of
the view to match the attributes stored in the nib file.

why does initializing subclasses require calling the super class's same init function?

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