Reader beware: this post is part of the older "Objective-C era" on Cocoa with Love. I don't keep these articles up-to-date so the code may be broken or superceded by newer APIs. There's some good information but there's also some opinions I no longer endorse – keep a skeptical mind. Read "A new era for Cocoa with Love" for more.
In this post, I build and run a Cocoa Mac application on the command-line. This might not sound like a very difficult goal but I'll attempt to follow an additional constraint: use as few tools, components, classes and even lines of code as possible; truly minimalist Cocoa programming. The goal is to create an application that qualifies as a proper Mac application (including a menubar and a window) but without using Xcode, without an Info.plist file, without NIB files, without Interface Builder and without even using a text editor other than the Terminal itself.
For this post, I was inspired (in a somewhat tangential way) by Amit Singh's Crafting a Tiny Mach-O Executable. In his article, Amit Singh crafts a 248 byte Mach-O executable in an effort to make the smallest executable possible on Mac OS X (he also presents a 165 byte version but this no longer runs in Snow Leopard).
A Cocoa application would never really get this small since "Cocoa" implies a large amount of linkage and runtime overhead in the executable (and I don't really want to start coding any of that by hand in assembly). But it inspired me to consider how you might reduce scale and complexity for a Cocoa App; what would be a minimalist Cocoa Mac application?
Of course, the answer in this case is entirely dependent on what criteria you require for a program to be considered a "Cocoa Mac application". I decided that a Cocoa Mac application must:
NSApplicationto run the main event loop
- Display a menubar with an application menu and a quit item which must correctly terminate the application
- Display an
- Bring the main window to the front on startup like a normal application
- Code and program should raise no warnings or errors (preferrably no poor coding practices either but that's subjective)
Ordinarily, these aren't difficult objectives — they are part of the standard Xcode Cocoa application template — but there's actually a huge amount of content described in the default template. Just because you didn't add the different pieces yourself doesn't make it minimalist.
If you're extreme enough to believe that you could program directly in hex, then you might consider the following to be the baseline for minimalist Mac OS X programming:
That's Amit Singh's 248-byte program in raw hex, saved to a file by "xxd", marked as executable using "chmod", run and the result displayed (it returns the exit condition 42).
Realistically though, all I can read are the first 5 bytes "CEFAEDFE07" which are the magic value for a Mach-O executable plus the CPU type for X86 CPUs. While historically, people certainly have programmed in hex, I would never write a Mac OS X program like this and I doubt anyone else would either. So piping Amit's actual assembly directly through nasm is probably a more realistic baseline for minimalist Mac OS X programming.
I'm not going to fight through this in assembly though (it's an Objective-C blog, after all), so I'm going to need a compiler to progress from here.
Creating, building and running the smallest possible C program looks something like this:
The options used with gcc might look a bit odd but they are: "-x c" (build as C code) and "-" (take input on standard in). The default output file name of "a.out" is acceptable for now.
While technically this builds a valid program and looks simpler than the block of hex up above, it actually does less than the previous program did — it doesn't even deliberately return a specific value (instead I get 252 which is probably just junk from the stack since
main implicitly returns an
int that we aren't returning).
It's a little better to actually return a value. While it's not much, you can at least claim that the program "does something".
Great, we've returned the default success value.
The smallest Objective-C program is the same as the smallest C program, just change the command-line so gcc builds Objective-C:
but the gcc output is literally the same. I'll need to use at least one class to truly claim this is Objective-C:
Making a Foundation program is no harder, we just link against the Foundation framework instead of libobjc. I could use literally the same program but here's one with a proper
NSAutoreleasePool, logging and process information:
NSProcessInfo has no problems getting the command-line arguments without
main needing to handle them.
With an AppKit application, the
main function looks a little simpler because
-[NSApplication run] creates its own autorelease pool (although I'll need to bring it back again later to work outside
Unfortunately, while this AppKit program runs, it doesn't present a user-interface and doesn't quit unless we kill it. Not very satisfying.
Satisfying the requirements
In Snow Leopard, programs without application bundles and Info.plist files don't get a menubar and can't be brought to the front unless the presentation option is changed:
Next, we need to create the menu bar. You don't need to give the first item in the menubar a name (it will get the application's name automatically):
Then we add the quit item to the menu. Fortunately the action is simple since
terminate: is already implemented in
NSApplication and the
NSApplication is always in the responder chain.
Finally, all we need to do is create a window and activate the application:
In keeping with the pattern of the rest of the post, here's the entire program as a single, command-line executable statement:
That's an entire Cocoa Mac application that you can copy onto the clipboard, paste directly at the prompt in Terminal, hit return and it'll run.
At 9352 bytes when compiled (exact size may change from computer to computer), my simple Cocoa program is nowhere near as small as the assembly written Mac OS X baseline of 248 bytes. At 27 lines long, it might not immediately seem "minimalist", either. However, it does satisfy the requirements I set for being a proper Cocoa Mac application.
The reality is that the things done under-the-hood by the default application templates, by the Xcode build system, by Interface Builder and by
NSApplication itself in conjunction with the Info.plist file are significant. To replicate the required subset of their functionality requires at least some work. Compared to the standard Xcode Cocoa application template's 85782 bytes for the entire compiled bundle, my app's goal of Cocoa minimalism seems far more successful.
Reducing the size of the executable wasn't my primary goal, though. I was aiming to reduce the number of tools, components, files and classes required for a genuine Cocoa Mac application. Relative to the standard suite of tools and collection of files involved in building a Cocoa Mac app, 27 lines of code processed using echo, gcc and a bash terminal represents a significant simplification.
I welcome further suggestions about how to satisfy the same requirements with less.
The overhead of spawning threads (a performance experiment)