Properly initing NSWindowController subclasses
I had a devil of a time with this, so I'm
blogging it in case someone else has the same problem.
If you are writing an NSDocument-based app with a
single nib, this entry really isn't for you. You won't have any problems,
because Cocoa takes care of it for
you.
If, however, you find yourself
working on an app with multiple nibs, you may be glad I wrote this. I am a
contract programer on an application with a great many nibs. I just added my
first nib to the app, and I was running into strange crashes. Although I
suppose I should have known this, I didn't know that I was responsible for
properly disposing of all top-level objects in any nib I load. The top-level
objects, by the way, are all those objects shown in the nib window. The problem
for me came in with bindings and the File's Owner. I was binding things to the
files owner, which seemed reasonable, and in fact is, I
believe.
The problem is that each of those
bindings caused an additional retain on my class, so that when I tried to
release it, it didn't go away, because there were more retains. Just adding
releases was no solution either. And, I wasn't releasing all those top-level
objects. Nasty circular problem. What to
do?
NSWindowController to the rescue.
Turns out, if you make your controller class a subclass of NSWindowController,
all those retains are taken care of, as well as releasing the top-level nib
objects. That is, as long as you let NSWindowController do the nib loading. But
how do you do that?
Not hard, really.
First, you do know that you need to init the superclass in all your inits,
right? Usually something like this:
-(id)
init
{
self =
[super init];
if (self != nil)
{
//finish
specific
initing
}
return
self;
}
Some
people like to return immediately if self is nil. For some reason, I like this
better. Of course, your init can be more complex, with variables passed to you
and such.
So, if you are loading a nib
with your NSWindowController subclass, your super init will look
like
self =
[super initWithWindowNibName:aNib];
or
self = [super
initWithWindowNibPath:aNibPath
owner:self];
The second worked best for
me. Other than recording my input variables, I also finished my initialization
of my class in the awakeFromNib method. One thing more. initing doesn't
actually load the nib. You need to call window on the class. I found it best
to do that in the method that inited my class in the first place.
Thus:
MyClass * aMyClass = [[MyClass
alloc] initWiththis:this
andThat:that]];
[aMyClass
window];
This may not be the best way to
do this. It worked for me, and I thought it worth recording. Comments are
available for anyone to point out my obvious errors.
Posted: Fri - January 9, 2009 at 10:35 PM