Working without a nib, Part 8: The nib awakens

October 17, 2016

Reboots are de rigueur these days, so I've decided to reboot my series "Working without a nib". Four score and minus seventy-two years ago, I brought forth on this internet a new blog post, conceived in levity, and dedicated to the proposition that no nibs are created. Since then, much has changed in the Mac world. Except of course the Macs themselves, which haven't been updated in a long time. Fortunately, the source code in my Nibless 2.0 project still works on the latest version of Mac OS X. (I hear some murmuring in the audience.) The use of the undocumented method setAppleMenu: has become redundant and unnecessary (or is it the other way around?) starting with Mac OS X 10.6 Snow Leopard (AKA the last good version). From the AppKit release notes:

In Leopard and earlier, apps that tried to construct a menu bar without a nib would get an undesirable stubby application menu that could not be removed. To work around this problem on Leopard, you can call the undocumented setAppleMenu: method and pass it the application menu, like so:

[NSApp setAppleMenu:[[[NSApp mainMenu] itemAtIndex:0] submenu]];

In SnowLeopard, this workaround is unnecessary and should not be used. Under SnowLeopard, the first menu is always identified as the application menu.

Unfortunately, the Xcode project for Nibless 2.0 is a lost cause. The latest Xcode fails to convert the project from Objective-C garbage collection (member garbage collection? member Chewbacca?) to ARC. (More on ARC in a minute.) I was finally able to get it to work by manually editing the build settings in the project.pbxproj file, but that's not recommended. Actually, none of this is recommended, but never mind that. Let this be another lesson to you that undocumented file formats such as xib and pbxproj are to be avoided as much as possible. Get all of your build settings out of pbxproj and into an xcconfig file, which is … an undocumented file format. The advantage of xcconfig, in technical terms, is that it's more plain text-y, less XML/plist-y.

ARC, or Automatic Reference Counting (Otto who?), was introduced after the last blog post in this series. ARC has been a nice addition to the toolbox, and hopefully it won't go the way of the dodo or ObjC GC (not to mention ObjC). ARC does have a few awkward quirks, though. I'm not going to call them flaws; some people would call them flaws. For example, the following code compiled with ARC will crash your app:

1    NSRect contentRect = NSMakeRect( 100.0, 100.0, 500.0, 500.0 );
2    NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask;
3    NSWindow *myWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:styleMask backing:NSBackingStoreBuffered defer:YES];
4    [myWindow makeKeyAndOrderFront:nil];
5    [myWindow close];

Can you spot which line of code is the problem? If you guessed line 3.5, then you cheated!

The problem is that you need to call [myWindow setReleasedWhenClosed:NO]. In pre-ARC code, this would not be surprising to old-school Cocoa coders. However, ARC does not even allow you to use the -release and -autorelease methods, so you might think that -setReleasedWhenClosed: would not be allowed either, or would at least be ignored. And you would be very wrong. The irony is that the crash occurs in -[NSAutoreleasePool drain], so I suspect that releasedWhenClosed is implemented as autorelease rather than release.

Most developers who use ARC will never run into this problem because in both ARC and non-ARC code, -[NSWindow releasedWhenClosed] is ignored for windows owned by an NSWindowController. Most developers would load a window from a nib with a window controller. But we're not most developers. We're the rebels.

May the nib be without you, always.