I was replacing the deprecated +(BOOL)loadNibNamed:owner:
method in our macOS app with a more standard initWithWindowNib
approach and came across the Apple guide about Document-Based App Programming Guide for Mac. One section particularly drew my attention: An NSWindowController Subclass Manages Nib Files
For records it say:
An
NSWindowController
object expects to be told what nib file to load (through its initWithWindowNib... methods) because it is a generic implementation of the default behavior for all window controllers. However, when you write a subclass ofNSWindowController
, that subclass is almost always designed to control the user interface contained in a particular nib file, and your subclass would not work with a different nib file. It is therefore inconvenient and error-prone for the instantiator of the subclass to have to tell it which nib file to load.This problem is solved by overriding the
init
method to call the superclass’sinitWithWindowNibName:
method with the correct nib name. Then instantiators just useinit
, and the controller has the correct nib file. You can also override theinitWithWindowNib...
methods to log an error, as shown in Figure 2-4, because no instantiator should ever try to tell your subclass which nib file to use. It is a good idea for anyNSWindowController
subclass designed to work with a specific nib file to use this technique. You should do otherwise only if you are extending just the basic functionality ofNSWindowController
in your subclass and have not tied that functionality to any particular nib file.
Pretty reasonable, let's try it.
Note: There is a question related to the same documentation section, but it has a different problem, asks a different question and is not using Modern Objective-C syntax.
My MainWindowDelegate.h now has:
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithWindow:(nullable NSWindow *)window NS_UNAVAILABLE;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
- (instancetype)initWithWindowNibName:(NSNibName)windowNibName NS_UNAVAILABLE;
- (instancetype)initWithWindowNibName:(NSNibName)windowNibName owner:(id)owner NS_UNAVAILABLE;
- (instancetype)initWithWindowNibPath:(NSString *)windowNibPath owner:(id)owner NS_UNAVAILABLE;
And MainWindowDelegate.m now has:
- (instancetype)init {
self = [super initWithWindowNibName:@"MainWindowWin"];
if (self) {
// Initialization code here.
}
return self;
}
The only available initializer from the AppDelegate
is correctly the init
(if I try to use initWithWindowNibName
then I get the error 'initWithWindowNibName:' is unavailable
). So far so good and it works pretty nicely.
But now I have a bunch of warnings...
For - (instancetype)init {
line
Designated initializer missing a 'super' call to a designated initializer of the super class
For self = [super initWithWindowNibName:@"MainWindowWin"];
line
Designated initializer invoked a non-designated initializer
If this is the suggested Apple approach to subclassing NSWindowController why all these warnings? Am I missing the point? How to fix the warnings and maintain only init
initializer availability?