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          


©