This article is designed to serve multiple purposes. First, it is to educate which I hope that it will. Secondly, it is a personal memory so that if (when!) I run into this issue again, I will be able to use Google to find this post and remember why I am an idiot and why the default settings for things are not always the best settings.
First as always, here is the project that goes with this article.
If you run this application you will see that it does basically nothing. The interesting part of this app is how it crashes. To crash this application, perform the following steps:
- Open the application.
- Select the Application menu.
- Select the About window.
- Close the About window.
- Open the Application menu again.
Cool huh!?! But wait, it gets even more interesting than that! Now lets take a look at the stack trace of this crash.
Thread 0 Crashed:
objc_msgSend + 24
-[NSMenu _enableItems] + 175
AppKitMenuEventHandler + 670
DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 1181
SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 405
SendEventToEventTarget + 52
SendMenuOpening(MenuSelectData*, MenuData*, double, unsigned long, __CFDictionary*, unsigned char, unsigned char*) + 797
DrawTheMenu(MenuSelectData*, __CFArray**, unsigned char, unsigned char*) + 231
MenuChanged(MenuSelectData*, unsigned char, unsigned char) + 451
TrackMenuCommon(MenuSelectData&, unsigned char*) + 1418
MenuSelectCore(MenuData*, Point, double, unsigned long, OpaqueMenuRef**, unsigned short*) + 279
_HandleMenuSelection2 + 383
_HandleMenuSelection + 53
_NSHandleCarbonMenuEvent + 244
_DPSNextEvent + 1834
-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
-[NSApplication run] + 795
NSApplicationMain + 574
main + 30 (main.m:14)
start + 54
Note: I removed the memory addresses since they are not relevant to the discussion.
Now, do you see what line in my application that caused this to crash? Go ahead and look, I’ll wait.
Figure it out? If you have, then you are far wiser than me and I should be reading your blog.
The cause of this crash is actually in the nib and not in the code anywhere. If you open the nib you will see that I linked the About Menu NSMenuItem’s action directly to the About NSWindow’s makeKeyAndOrderFront: method. Makes sense since there is nothing special about this window at all, it just presents the app’s brag wall and that’s it. The error, however, is in the default settings of this window. On the first tab of the settings for this window you will see the option to “Release When Closed” and that is the evil little guy behind this crash.
Note that this is the default setting for new windows and if you forget to turn this off (when appropriate) then bad things like this will happen. In this example, when you close the About window, the nib helpfully calls release on that window and deallocates the memory it held. Then when the NSMenu attempts to reference it again (clicking on the app menu a second time) you get a EXC_BAD_ACCESS (SIGSEGV)
Pretty huh? The interesting part of this is because the setting is in the nib, none of your code gets called and therefore it makes it quite difficult to track this crash down. Fortunately for me, I had a fantastic beta tester who was able to show me how to reproduce the crash and thereby narrow it down to this problem.