Cocoa Tutorial: How to Crash Cocoa
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.
So, is there a way to have it release when closed, but allocate a new instance when needed?
I had this problem, fortunately it was in a very small app and i quickly figured out where the problem was. I didn’t think the stack was much use though!
Yes there are plenty of ways to get around this issue in that direction. However, it involves code which I was trying to avoid.
Any time you have a SIGSEGV you have a memory problem. They generally come in three forms:
1) You accesses freed/deallocaed memory
2) You used an un-initialized pointer
3) You have a buffer over-run memory corruption
The first thing to do in a Cocoa application is turn on NSZombieEnabled. http://www.cocoadev.com/index.pl?NSZombieEnabled “with zombies enabled, messages to deallocated objects will no longer behave strangely or crash in difficult-to-understand ways, but will instead log a message die in a predictable and debugger-breakpointable way.”
I’m just starting Cocoa development, and I imagine you’ve already saved me a bunch of time. :)