8
Mar
2008
 

Cocoa Tutorial: How to Crash Cocoa

by Marcus Zarra

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.MenuCrash.zip

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:

  1. Open the application.
  2. Select the Application menu.
  3. Select the About window.
  4. Close the About window.
  5. Open the Application menu again.

BOOM!

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.

window_attributes.pngThe 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.

Comments

Paul says:

So, is there a way to have it release when closed, but allocate a new instance when needed?

PixelSmack says:

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!

Marcus Zarra says:

Paul,

Yes there are plenty of ways to get around this issue in that direction. However, it involves code which I was trying to avoid.

erik says:

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.”

sdfisher says:

I’m just starting Cocoa development, and I imagine you’ve already saved me a bunch of time. :)