Archive for the ‘Xcode’ Category

Stabs is deprecated

Sunday, March 9th, 2008

This post is dedicated to E. Gary Gygax, the second greatest corrupter of youth in history. It’s about D&D, that is, DWARF and dSYM. As of 2008-02-27, the STABS debugging symbols format has been deprecated by Apple. The default value for the DEBUG_INFORMATION_FORMAT build setting in Xcode projects had been stabs, but now it’s time to move on. (I’m talking to you, Justin Long.) Our other options are dwarf or dwarf-with-dsym. Also cake or death.

With STABS, you could build the release version of your app with debugging symbols, make a copy of the executable MyApp.app/Contents/MacOS/MyApp to keep, strip the executable for shipping, and then use the unstripped executable for symbolizing crash reports by giving a space-separated list of stack trace addresses to the command-line tool atos. Unfortunately, atos cannot currently serve this purpose with DWARF. Unlike STABS, DWARF does not include the debugging symbols in the executable itself but merely includes references to the intermediate object files, which do contain debugging symbols. You can usually find these .o files in a sub-directory of the build/MyApp.build directory. If you delete the object files after building with dwarf, you won’t be able to step through your app’s code. (With stabs, the object files are refuse.) You also won’t be able to step through the code if you strip debugging symbols from your app, even if you keep the object files, because the references to the object files will be gone from the executable.

To avoid losing the debugging symbols for your app after stripping, you want to use the option dwarf-with-dsym. The DWARF with dSYM option performs an additional step beyond ordinary DWARF: it creates a separate MyApp.app.dSYM file that contains all of the debugging symbols for your app. In fact, the DWARF with dSYM option allows you to step through your code regardless of whether the executable is stripped! This is possible because gdb will look for the .dSYM file in the same directory as your app. It doesn’t need to know the name or location of the object files. If you don’t strip debugging symbols, you can use either the .o files or the .dSYM file for debugging, but for the local debug build of your app there’s no point in using dSYM, since that would just prolong your build time. You have better things to do than wait for builds, such as writing comments on Slashdot.

The trouble with atos is that it does not reliably find debugging information in .dSYM files for stripped executables. Although Apple’s documentation (as of 2007-04-02) says, “If you’re using DWARF dSYM files, you must be using the version of atos included in Xcode 3 (Mac OS X version 10.5)”, Apple’s engineers say, “The underlying framework that atos uses doesn’t support loading symbol names from dSYM files in Leopard.” In my testing, however, there doesn’t seem to be a difference between Leopard and Tiger, at least not with Xcode 2.5 on Tiger. On both Leopard and Tiger, atos successfully loads symbol names from .dSYM files (I deleted the .o files) for unstripped executables. For stripped executables, in contrast, atos frequently fails to load the symbol names, or even gives inaccurate results.

The CrashReporter Technical Note suggests loading your app and its .dSYM in gdb to translate stack trace addresses from crash reports. That’s like having to start your car in order to read the odometer. (Oh wait, I have to do that, Nissan!) An alternative method is the command-line tool dwarfdump. It requires only the .dSYM file, not a copy of your app, and its --lookup option will do the same job as gdb without the overhead.

Please note that by breathing, blinking, or moving at all, even to command-w this page, you thereby register your agreement not to disclose or discuss this information anywhere with anyone at any time, no matter the duress, torture, or water-boarding you may undergo to extract it. This agreement holds despite the fact that the information is publicly available on the internet for every person in the world to read. Failure to uphold this agreement will result in multiple, painful cat scratches, in certain cases leading to cat scratch fever.

Logging in Leopard

Sunday, January 6th, 2008

The release of Leopard has given third-party developers a lot to do: attempting to restore features lost from Tiger, for instance. (By the way, where is the second party, and why am I never invited?) My friend Rainer Brockerhoff has provided a way, or Quay, to display hierarchical popup menus in the Dock again. One of my most missed features in Leopard is using NSLog to spew output exclusively to Xcode’s console log. When you debug or run your app in Xcode on Tiger, you can put NSLog calls everywhere without worrying about polluting console.log. In my opinion, console.log is only for important messages and errors. I frequently ask users to consult it if they’re experiencing a problem with an app. Either that or the Oracle at Delphi.

Leopard dispenses completely with console.log, though there is a “Console Messages” database query in Console. Whereas on Tiger stdout and stderr standardly go to console.log, on Leopard they boldly go to system.log (as well as to the “Console Messages” query). On either version of Mac OS X, Xcode redirects stdout and stderr to its own console log, so they don’t appear in Console at all.

According to the documentation, NSLog sends a message to stderr. This is true for Tiger, and it’s also true for Leopard, but Leopard’s NSLog has the additional behavior of sending a message to system.log regardless of whether stderr is redirected. Thus, when you debug or run your app in Xcode (these may amount to the same thing in Xcode 3), messages from NSLog appear both in Xcode’s console log and in system.log! Curiously, there is no duplication of NSLog messages in system.log when stderr is not redirected.

If you prefer to keep your debug output out of system.log, the workaround for this new NSLog behavior is to abandon NSLog for debugging purposes on Leopard. :-( After much experimentation with asl, I realized that our old faithful printf would work. Since printf writes to stdout, its output is redirected by Xcode. Plus, when you’re debugging your app in Xcode you don’t really need NSLog to tell you the name of your app, the date, or your shoe size.

A limitation of printf is that it doesn’t handle the format specifier %@ for an Objective-C object. With Cocoa, therefore, we want an Objective-C wrapper around printf (like, um, NSLog). If you add the following code to your target’s .pch file, you’ll have an Objective-C debug logging function JJLog available throughout your target’s code. To enable logging in your app’s debug build, just add JJLOGGING to the GCC_PREPROCESSOR_DEFINITIONS setting (AKA “Preprocessor Macros”) in the debug build configuration.


#ifdef __OBJC__
	#import <Cocoa/Cocoa.h>
	#ifdef JJLOGGING
		#define JJLog(...) (void)printf("%s:%i %s: %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String])
	#else
		#define JJLog(...)
	#endif
#endif

In your app’s release build, the debug function is a NOP that the compiler will almost certainly optimize out. This conditional code should not cause problems when using GCC_PRECOMPILE_PREFIX_HEADER, because Xcode already generates a separate precompiled prefix header for each build configuration. See the .pch.gch.hash-criteria files in /Library/Caches/com.apple.Xcode.###/SharedPrecompiledHeaders.

You can send gobs of gab to JJLog without repercussion or remorse. However, you’ll still want to use NSLog (sparingly, please) for runtime errors in your release build. Now to continue in the spirit of this post, I’ll redirect the epilogue to /dev/null.

Embedding frameworks in loadable bundles

Saturday, August 11th, 2007

While I worked for Marko Karrpinen & Co. I only made one commit to BaseTen, but as Sappho would say, that one was a doozie! BaseTen is an open source Cocoa framework for PostgreSQL. It has an API resembling Apple’s Core Data framework, which uses SQLite. You can check out the source and build BaseTen.framework as well as the optional BaseTenAppKit.framework and an Interface Builder palette, BaseTenPalette.palette. (By the way, I abhor Interface Builder. Or at least Interface Builder 2. I’ll reserve judgment on Interface Builder 3 until I learn more about it, and only then will I abhor it.) The frameworks are designed to be embedded within your application’s bundle, in the standard location for embedded frameworks: the directory Contents/Frameworks.

An app needs to know how to locate linked frameworks at runtime, so at compile time the app’s executable gets a record of each linked framework’s install name. An install name is, as you should expect by now, not a name. It’s a path, namely, the location of the dynamic library containing the framework’s code. To be exact, the install name is where the library should be at runtime, for a library wouldn’t even need an install name if it just indicated where the library actually is at compile time. Install names enable you to target Panther, for example, while still compiling with Tiger. You can use the command-line otool -D to see that the install name of

/Developer/SDKs/MacOSX10.3.9.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

is

/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

Using absolute paths for install names is fine when your app links against system frameworks, which reside in pre-determined locations, but absolute paths won’t suffice when your app links against embedded frameworks, because the app could be installed almost anywhere in the file hierarchy, e.g., ~/Desktop or /Volumes/MyDistributionDmg. That’s why an embedded framework needs a relative path install name. The BaseTen and BaseTenAppKit projects achieve this by setting the build setting INSTALL_PATH (what else would you do with a build setting but set it?) to @executable_path/../Frameworks. The relative @executable_path is the path to the Contents/MacOS directory in your application’s bundle. When BaseTen is built with that build setting (to answer my last question, you would build with it), the install name of the framework becomes

@executable_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen

as you can verify with otool -D. Thus, when your app links against BaseTen.framework and records the install name, it can find the framework in its own bundle at runtime.

BaseTen’s IB palette needs to use the BaseTen frameworks too. The problem, however, is that if the frameworks are built to be embedded in an application, they aren’t configured correctly to be embedded in the palette. When Interface Builder launches it will fail to load the palette, logging an error:

Interface Builder[29996] *** -[NSBundle load]: Error loading code /Users/jeff/Library/Palettes/BaseTenPalette.palette/Contents/MacOS/BaseTenPalette for bundle /Users/jeff/Library/Palettes/BaseTenPalette.palette, error code 4 (link edit error code 4, error number 0 (Library not loaded: @executable_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen
  Referenced from: /Users/jeff/Library/Palettes/BaseTenPalette.palette/Contents/MacOS/BaseTenPalette
  Reason: image not found))

The reason that the image is not found — the reason behind the reason — is that the executable in this case in not actually BaseTenPalette but rather Interface Builder itself, which is trying to load the palette. The @executable_path leads to

/Developer/Applications/Interface Builder.app/Contents/MacOS

but BaseTen is embedded in

~/Library/Palettes/BaseTenPalette.palette/Contents/Frameworks

so the install name doesn’t locate the framework at runtime.

In Tiger, the relative @loader_path was introduced to supplement @executable_path. The @loader_path is relative to the image loading the dynamic library, wherever that image may be. Thus, if we change the install name of BaseTen to

@loader_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen

and the install name of BaseTenAppKit to

@loader_path/../Frameworks/BaseTenAppKit.framework/Versions/A/BaseTenAppKit

then BaseTenPalette should be able to locate the frameworks when Interface Builder launches. Problem solved, right?

If you’ve already skipped ahead to the end of this post, you’ll know that the problem is not solved. (Spoiler alert: Harry drops out of school to follow Trey Anastasio.) We’ve eliminated one error only to find another:

Interface Builder[2599] *** -[NSBundle load]: Error loading code /Users/jeff/Library/Palettes/BaseTenPalette.palette/Contents/MacOS/BaseTenPalette for bundle /Users/jeff/Library/Palettes/BaseTenPalette.palette, error code 4 (link edit error code 4, error number 0 (Library not loaded: @loader_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen
  Referenced from: /Users/jeff/Library/Palettes/BaseTenPalette.palette/Contents/MacOS/../Frameworks/BaseTenAppKit.framework/Versions/A/BaseTenAppKit
  Reason: image not found))

Whereas BaseTenPalette can now find BaseTen at runtime, BaseTenAppKit cannot. They both have a record of BaseTen’s install name as

@loader_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen

but they don’t have the same @loader_path.

At this point, you may throw up your hands and throw in the towel, exclaiming Alas, BaseTen cannot have two install names! — or some such exclamation perhaps not suitable for children. (Oh rat farts!). Yet your exclamation would be in vain, because the install name of the dynamic library doesn’t matter after linking. All that matters is the install name recorded in the linked executable, and that can be forged.

Apple provides the nefarious command-line install_name_tool to forge install names for dylibs and give them fake id’s. This is how frameworks get into bars, since there are very few that are twenty-one years old. You can examine the details of my fix in the BaseTen Trac, but basically what I did to allow BaseTenAppKit to find BaseTen was to run the following command in a build phase script for BaseTenPalette:


install_name_tool -change \
	"@executable_path/../Frameworks/BaseTen.framework/Versions/A/BaseTen" \
	"@loader_path/../../../../Frameworks/BaseTen.framework/Versions/A/BaseTen" \
	"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/BaseTenAppKit.framework/Versions/A/BaseTenAppKit"
	

I discovered the correct install name through a stroke a genius, or to put it another way, trial and error. The @loader_path for BaseTenAppKit turns out to be

~/Library/Palettes/BaseTenPalette.palette/Contents/Frameworks/BaseTenAppKit.framework/Versions/A

which makes sense in retrospect, but if you could guess ../../../.. on your first try, you’re a superfreak. Anyway, you can check the install names before and after with otool -l, or succinctly with otool -L.

Caveat developtor: for install_name_tool to work, you may need to build your frameworks with the option -header-pad_max_install_names. BaseTen already does this. See the man pages for more information, man.

Working without a nib, Part 5: Open Recent menu

Tuesday, July 10th, 2007

Judging from the search phrases in my referrer log and from posts to Apple’s mailing lists, quite a few people are interested in developing Cocoa applications without using nibs. I’ve heard the demand, and you’ll be pleased to learn that the wait is over. I have a sweet solution. Today I’m announcing a Cocoa nibless SDK. You can download the SDK immediately — from the web! Specifically, from my blog. Just load this web page in Safari and select Save As… from the File menu.

(Don’t worry about me. I hear that the Chairman of the Board has a sense of humor. I’m sure that these two large gentlemen at my door are here to convey his appreciation of my wit and to deliver an invitation to lunch.)

(No! No! Stop, please! Not the iPod too! Have mercy!)

At the end of Part 4 of this series, I suggested that we needed to call setValue:@"NSRecentDocumentsMenu" forKey:@"name" to set the Open Recent menu. This is why they call me “Good Ol’ Oftenwrong”. Luckily, if you have a document-based application, Cocoa will generously create an Open Recent menu for you. All you need to do is put a menu item with the action @selector(openDocument:) in pretty much any menu, and Open Recent will magically appear as the next item in the menu. Now that’s a sweet solution!

If you want an Open Recent menu for a non-document app, on the other hand, you need to use an ugly hack. Although it was clear that the NSMenu ivar _name is set to @"NSRecentDocumentsMenu" for the Open Recent menu in a standard Cocoa MainMenu.nib, I couldn’t get the menu to populate with recent items in my nibless app even after setting _name. By pure trial and error, I discovered that you have to call _setMenuName:@"NSRecentDocumentsMenu" rather than setValue:@"NSRecentDocumentsMenu" forKey:@"name". (It was a natural choice after trying setName: and _setName:, which are not implemented by NSMenu.) The method _setMenuName: does set the _name ivar, but apparently it does some other crucial stuff too. Perhaps it asks a favor of the iGodfather. Anyway, I’ve updated my Nibless Xcode project to demonstrate this behavior.

In summary, we have succeeded (by we I mean the royal we) in creating a Cocoa application with a full main menu but without any nib (and without any error messages). For this purpose we’ve had to call two private methods, -[NSApplication setAppleMenu:] and -[NSMenu _setMenuName:], as well as poseAsClass: to override +[NSBundle loadNibNamed:owner:]. Not bad. And it’s taken us less than two months to reproduce what Interface Builder can do in less than two seconds. Isn’t this fun? The hardest part is finished, though. From now on, it’s just smooth sailing, on the Good Ship of Pyaray.

Oh, one more thing. Let’s dance! Anyway you want it.

Working without a nib, Part 5: No, 3!

Sunday, June 10th, 2007

For all you desperate souls waiting in line at the Moscone Center, and you more desperate souls waiting in line at MacRumors, take heart, because there’s something even more desperate than you — NSApplicationMain(). It’s so desperate to load a nib that it’ll take the first one it can find. When your application launches, NSApplicationMain() instantiates the NSPrincipalClass from your app’s Info.plist and calls +[NSBundle loadNibNamed:owner:] with the instance as the owner. This method in turn calls +[NSBundle bundleForClass:] with your NSPrincipalClass and -[NSBundle pathForResource:ofType:] with type @"nib". If your Info.plist contains no NSMainNibFile key, then the nib name and path arguments for those methods are nil. Why in the world would your Info.plist be missing NSMainNibFile? See Part 1 of this series. If that doesn’t answer the question, see Part 2. If that doesn’t answer the question, see Part 3.

When I set the NSPrincipalClass key to my custom NSApplication subclass, the corresponding bundle for that class is my app’s main bundle, so if there’s no nib in the bundle, the app fails to launch with the error, No NSMainNibFile specified in info dictionary, exiting. However, when I leave NSPrincipalClass as NSApplication, the corresponding bundle turns out to be /System/Library/Frameworks/AppKit.framework. If you send the message -[NSBundle pathForResource:nil ofType:@"nib"] to that bundle, it returns /System/Library/Frameworks/AppKit.framework/Resources/English.lproj/NSAlertPanel.nib, which is the first nib file in the English.lproj folder. As a consequence, NSApplicationMain() attempts to load NSAlertPanel.nib and set the file’s owner to your app’s NSApplication instance. That particular nib file contains several buttons with the action buttonPressed: targeted at the file’s owner, but unlike NSAlert, which is specified as the class of the file’s owner in the nib, NSApplication doesn’t implement buttonPressed:, so you get the error, Could not connect the action buttonPressed: to target of class NSApplication. Mystery solved! And I would have gotten away with it too, if it wasn’t for those meddling kids!

There are a number of ways to handle this problem. My preferred workaround, which I’ve implemented in the revised version of the Nibless project, is to set NSPrincipalClass to JJApplication, call [[JJBundle class] poseAsClass:[NSBundle class]] in main.m, and override an NSBundle method in JJBundle.m:


+(BOOL) loadNibNamed:(NSString *)aNibNamed owner:(id)owner {
    if (!aNibNamed && owner == NSApp) {
        // We're lying here. Don't load anything.
        return YES;
    } else {
        return [super loadNibNamed:aNibNamed owner:owner];
    }
}

We now return to our regularly scheduled WWDC speculation. (I predict that everyone in the audience will get a car.) If you are attending The Keynote on Monday, remember to bring plenty of Scooby snacks. If you’re playing the home game: every time Steve says “cool”, drink!

Working without a nib, Part 2: Also Also Wik

Monday, June 4th, 2007

I apologize for the fault in the subtitle. Those responsible have been sacked. What I intended to say was that I’ve discovered the Holy Grail of Cocoa hacks: how to create a functional Cocoa application without any nib. In Part 1 of this series, I suggested that you needed a nib for the main menu of the app; some would even argue that having a nib is the essence of a Cocoa app. It turns out that I was wrong, and some (they, the unspecified straw persons) were wrong too. I apologize for any faults in my previous post. Those responsible would be sacked, but those responsible for sacking have just been sacked.

As you may recall if you have a photographic memory or nothing better to do, the main problem with getting rid of nibs is setting the application menu. I had been using a minimal nib as a workaround, but now I have a reliable, though undocumented, solution to the problem. I came upon the solution by using gdb and JJApp (along with a herring) to override initWithCoder: while the main menu is loaded from a nib. The class NSMenu has a private ivar _name, declared in /System/Library/Frameworks/AppKit.framework/Headers/NSMenu.h, that is usually nil, but a few menus — including the application menu — have a string value for the ivar. Although there are no public accessors, we can set the value anyway through the magic of key-value coding. For example, I called setValue:@"NSMainMenu" forKey:@"name" on my main menu and setValue:@"NSAppleMenu" forKey:@"name" on my application menu. This must be done before the return of -[NSApplication finishLaunching], otherwise it will have no effect. I find applicationWillFinishLaunching: to be a good place to set your main menu.

Another caveat is that the old deleting-the-nib trick doesn’t work at all if you change NSPrincipalClass in Info.plist. When I set it to my NSApplication subclass, the app refuses to launch, complaining, No NSMainNibFile specified in info dictionary, exiting. Strangely, it works fine if I leave NSPrincipalClass alone and call [[JJApplication class] poseAsClass:[NSApplication class]] in main.m. A special treat is that Cocoa automatically adds the Special Characters item to the Edit menu at runtime. Thanks, Cocoa! You can see all of this yourself by downloading my sample Xcode project, Nibless. If you use my code in your app, I may sue you, or I may kiss you. In either case, it’s a risk you’ll have to take. (It!)

Despite the fact that my solution is for the most part unsupported by official API (consult the book of armaments!), there’s good reason to think that it’s stable and should survive Leopard at least. Apple is unlikely to remove an ivar from such an important class as NSMenu. More important, the value of the ivar seems to be the way that archived menus indicate their function to Cocoa and to Interface Builder, as reflected by the “Special Menus” setting in the Inspector. Try opening keyedobjects.nib with BBEdit, and you’ll see that the file is just a plist containing definitions of the objects in the nib.

In Part 5 of this series — sorry, Part 3 — I’ll investigate the cause of the log message Could not connect the action buttonPressed: to target of class NSApplication when launching without a nib. I’ll also attempt to populate the Open Recent menu. The Clear Menu item seems to work, but for some reason I can’t get items to appear in the menu. So, um, anything that you could do to help would be very helpful.

I’d like to end this post on a personal note. Many of my legions of fans have sent emails asking for more information about me: birth date, hobbies, pet peeves, dress size, etc. I prefer not to start a cult of personality, but I’ve decided that you, the heroes, deserve something. Thus, I’m going to share one particularly juicy tidbit. My favorite color is … blue. No, yellooooooooow!

Failed Slower

Saturday, May 19th, 2007

Normally I refrain from linking to other blog posts. I don’t really consider myself to be part of the blogosphere. (We are the blogocube. We will add your biological and technological distinctiveness to our own. Bring your own beer.) Instead, I figure that you can follow my Favorite Feeds, which you can find in the sidebar of my blog, in my favorite feed reader, which you can get from Vienna Downloads. By the way, I just updated my Favorite Feeds today.

In the case of Fail Faster by Terrence Talbot, resistance to linking was futile. His blog was not yet in my Favorite Feeds, though it is now, and more important, he stole my idea! (I actually have a file named ideas.txt.) Anyway, read the post, because it’s a good idea, naturally. I implemented the idea back in Vienna revision 446, so I guess I can’t complain that I didn’t have enough time to publish it. D’oh! I’ll just add my two cents, which by coincidence is my standard consulting fee. Another source of build failures is Xcode itself. Believe it or not, Xcode still has a few bugs. Sometimes the only way to get your project to build correctly is to Clean first. When you Clean, however, any copied resources are deleted, so if your Copy Resources phase is at the beginning of the build, you may be needlessly repeating the copy phase.

This post as you know it is over. Length is irrelevant. Closing tags, on the other hand, are relevant. You must add </p>. Comply.

Working without a nib, Part 1

Wednesday, May 16th, 2007

There are a number of reasons why you might want to build your application without a nib. As you may know, ‘nib’ is an acronym for no inspecting bindings. Anyone who uses version control (in other words, anyone except R.B.) can see in the diffs that nib files are rather opaque. The next Interface Builder, the one in Mac OS X 10.5 Leopard, may offer some improvements in this area, although I can’t really say yet. This is not to say that I’m under NDA. It’s not to say that I’m not under NDA either. I am in fact under NDA, so many NDA it makes my head spin. I can’t even say how many NDA. This is not to say that my NDA prevent me from saying how many NDA I’m under. I just don’t know. Anyway, I don’t have a Leopard seed. Even if I did have Leopard, which I don’t, it isn’t scheduled for release until October, and besides, many developers will still want to support Tiger at the very least for years to come. Thus, my topic is not necessarily obsolete at startup, unlike the computer you just bought.

It is indeed possible to to run a Cocoa application entirely without a nib: create a Cocoa application project in Xcode, delete MainMenu.nib, and remove the NSMainNibFile entry from Info.plist. (You do get the log error Could not connect the action buttonPressed: to target of class NSApplication.) You can allocate your own objects in main(), though you must do that before calling NSApplicationMain(), not after. (It’s too late, Jim, he’s dead already.) Remember, Jim, main.m is just another Objective-C source file. Objects that you allocate before NSApplicationMain() can even register to receive notifications such as NSApplicationDidFinishLaunching, so you can hook into the launching process and perform whatever setup you need.

For all practical purposes, however, you can’t really work without a nib. The problem is the main menu, or more specifically, the application menu. In a normal Cocoa application, the application menu is what would be returned by [[[NSApp mainMenu] itemAtIndex:0] submenu]. In MainMenu.nib of a default Xcode Cocoa application project, the application menu is the submenu for the item NewApplication. Actually, it doesn’t matter what the title is of this menu item, because regardless of how you set it in Interface Builder, Cocoa replaces the title at runtime with an empty string, and the name of the application boldly goes on the menu bar. The title of the application menu itself is Apple, which is highly misleading, considering that its left-side neighbor on the menu bar has an Apple icon at the top. Just to be clear, the Apple menu is not the menu with an Apple. Got it?

What prevents our nibless existence is that the application menu refuses to be set programmatically. If there is no main nib file or no main menu in the main nib, Cocoa creates an application menu automatically, and there appears to be no API to access it. You could create your own NSMenu with an NSMenuItem at index 0 having a submenu with the title @"Apple", but if you try to set it as the main menu with -[NSApplication setMainMenu:], all of your submenus, including your Apple menu, will appear to the right of the automatically generated application menu. Like a poor marksman, you’ll keep missing the target!

When faced with a no-win situation, what do we do? Cheat! My workaround to the problem is to keep MainMenu.nib in the Cocoa project but leave only the most minimal main menu possible. The Window can be deleted entirely, and in the MainMenu, delete everything except the NewApplication menu item and its submenu, which is listed as the Application Menu in the Inspector. The submenu’s menu items can be deleted. If you allow the main menu to come from the nib, then the Apple menu will automatically become part of -[NSApplication mainMenu], and you can populate it and the rest of the main menu yourself. The most elegant way of populating the main menu, in my opinion, would be to subclass NSApplication, set your subclass as NSPrincipalClass in Info.plist, call [self mainMenu] in your subclass awakeFromNib, and add items to the returned menu. (The NSPrincipalClass of your app needs to be a subclass of NSApplication, otherwise your app crashes on launch with a selector not recognized exception.)

In Part 2 of the series, I’ll attempt to add some functionality to my currently useless, empty app. My ongoing mission will be to explore the strange new world of life without (much of) a nib. It’s a difficult concept, perhaps not very logical, but we learn by doing.

Backing up your app’s support files

Sunday, April 22nd, 2007

I’m not only the co-President of the Vienna Club for Men, Women, and Others: I’m also a client. Before I became a Vienna developer, I was a user. During development and debugging of the app, I don’t want to corrupt my user data, e.g., database and preferences, so I back them up before I do anything dangerous. Believe me, anytime I launch Xcode, it’s dangerous.

A long time ago I created a set of Automator actions to back up the files and restore them afterward. Yes, I’m the one of the five or so people in the world who actually use Automator. (Otto who? Auto Parts?) However, after I bought a laptop — or portable computer not for use on your lap under any circumstances, on penalty of burnt naughty bits, as they say — and I started using a FileVault account, I discovered that Otto and FileVault don’t play well together. (They can’t agree on who has to be Samwise.) Thus, I needed a new method of backing up and restoring my Vienna files.

Presto Change-o, Hocus Pocus, I created a custom Xcode project to do the job. At first I was just going to provide it to Vienna developers, but then I decided that I could (semi-)easily make it configurable to handle the files of other apps, since application support files tend to reside in standard locations. Later, greed got the better of me, and I decided to charge $50 a head for my creation. Finally, sanity got the better of me, and I’m making the source available for download. You can still send me $50 if you like, $100 if you have two heads.

The project is called BackupAppFiles, which is pretty boring, I admit, but hey, my parents named me Jeff Johnson. Anyway, BackupAppFiles comes configured to handle Vienna support files. To handle the files of a different app, just change the string constants in BAFConstants.m to match the file names. You can compile different versions to handle different sets of files on the same computer, but you’ll need to change the PRODUCT_NAME build setting for each version, because the location of BAF’s own support files depends on that setting.

The standard disclaimers apply once again. I shall not cause harm to any vehicle nor the personal contents thereof, etc. Please back up your files before using BackupAppFiles. (Yes, I recognize the irony there. No need to point it out, really.) I take no responsibility if BackupAppFiles erases your hard drive or makes your head explode, though I will refund your $50 in the latter case. (It happens sometimes. People just explode. Natural causes.) Let me know if you have any questions, suggestions, or complaints. About BackupAppFiles, that is. Or a plate of shrimp. I’ll also take bug reports and patches, just no feature requests. Now let’s go get sushi.

Everything you always wanted to know about NSApplication

Saturday, March 10th, 2007

It seems that waiting for me to write a new blog post is like waiting for Larry David to write a new TV show. By the way, season one of Lap Cat Software Blog is available now on DVD for the low price of free ($35 shipping and handling). For those of you who don’t care for reruns, the wait is finally over: today is the season two premiere blog post! If you recall, in last season’s finale I was abducted by aliens and replaced by an evil clone. As we begin this season, I’m still the clone, but I’ve mellowed and become Lieutenant Governor. Confused?

I was confused about calling -[NSObject performSelector:withObject:afterDelay:] during application launch. Specifically, I was interested in a delay of (NSTimeInterval)0.0. When exactly, if ever, would the selector be performed? Is there a chance that user events could intervene? According to the documentation, The run loop for the current thread must already be running and in the default mode (NSDefaultRunLoopMode) for aSelector to be sent. For the purposes of investigation, I created an Xcode test project. In typical overkill fashion, I decided that getting to the bottom of the issue would require overriding every public method of NSApplication, NSMenu, NSRunLoop, NSThread, and NSWindow, along with some selected super and protocol methods. I threw in NSAutoreleasePool for good measure. In my main(), I put the following commands before the call to NSApplicationMain():

[[JJApplication class] poseAsClass:[NSApplication class]];
[[JJAutoreleasePool class] poseAsClass:[NSAutoreleasePool class]];
[[JJMenu class] poseAsClass:[NSMenu class]];
[[JJRunLoop class] poseAsClass:[NSRunLoop class]];
[[JJThread class] poseAsClass:[NSThread class]];
[[JJWindow class] poseAsClass:[NSWindow class]];

There was probably an easier way to do this, but I did what I did. I yam what I yam. You can download my project. As usual, it’s released under the SHAG license.

Leaving out many details, here is the basic sequence of events when you launch a Cocoa application. Your results may differ, so talk to your doctor before betting your life on this sequence.

  1. +[NSApplication sharedApplication].
  2. -[NSMenu initWithCoder:]
  3. -[NSWindow initWithContentRect:styleMask:backing:defer:]
  4. -[NSApplication setMainMenu:].
  5. -[NSApplication setWindowsMenu:].
  6. -[NSApplication setServicesMenu:].
  7. awakeFromNib.
  8. Enter -[NSApplication run]. This is the point of no return. That is, you never return from this method.
  9. Enter -[NSApplication finishLaunching].
  10. NSApplicationWillFinishLaunchingNotification.
  11. -[NSApplication makeWindowsPerform:@selector(_registerDragTypesIfNeeded) inOrder:NO].
  12. Return from -[NSApplication finishLaunching].
  13. Enter -[NSApplication nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:kCFRunLoopDefaultMode dequeue:YES].
  14. -[NSApplication activateIgnoringOtherApps:NO].
  15. NSApplicationDidFinishLaunchingNotification. Notice that the app already returned from finishLaunching a while ago (relatively speaking).
  16. -[NSApplication makeWindowsPerform:@selector(_visibleAndCanBecomeKey) inOrder:NO].
  17. Return NSEvent with type NSAppKitDefined, subtype NSApplicationActivatedEventType, from -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:].
  18. Enter -[NSApplication sendEvent:] with the same event.
  19. NSApplicationWillBecomeActiveNotification.
  20. NSApplicationDidBecomeActiveNotification.
  21. Return from -[NSApplication sendEvent:].

A few interesting notes:

  • NSThread. As you would expect, all of the application’s methods are called on the one, main thread. I’ve defined the macro JJTHREADLOGGING to verify this. For the main thread, which is returned by +[NSThread currentThread], none of the methods +[NSThread alloc], +[NSThread allocWithZone:], or -[NSThread init] are ever called.
  • NSAutoreleasePool. An autorelease pool is not created until after -[NSApplication finishLaunching] returns. If you call autorelease in your application delegate method applicationWillFinishLaunching:, an autorelease pool will not be created! Memory leak, anyone? The methods +[NSAutoreleasePool allocWithZone:] and -[NSAutoreleasePool init] are called many times, but they always return the same object, while -[NSAutoreleasePool release] and -[NSAutoreleasePool dealloc] are never called.
  • NSRunLoop. The run loop for the main thread is created in -[NSApplication setServicesMenu:]. Strangely, the run methods for the run loop ( -[NSRunLoop acceptInputForMode:beforeDate:], -[NSRunLoop limitDateForMode:], -[NSRunLoop run], -[NSRunLoop runMode:beforeDate:], -[NSRunLoop runUntilDate:] ) are never called.

If you’d like to try my project for yourself or modify it, I’d be very interested to hear about what you learn. I haven’t really investigated application termination, for example. You may have noticed that I also haven’t addressed how -[NSObject performSelector:withObject:afterDelay:] works during launch. I’m such a tease! These questions — and many others — will be answered in the next episode.