Archive for the ‘Cocoa’ Category

Local variables are free

Saturday, December 19th, 2009

This is part II of my irregularly scheduled series on compiler optimization. In part I, I explained how the compiler can optimize away return statements, resulting in missed breakpoints. My given workaround to that problem, though effective, was very ugly and architecture-dependent, much like Cowboys Stadium.

(gdb) break *0x00001fc5 if $eax != 0

Although there’s not much we can do to prevent the compiler optimization, we can greatly simplify our conditional breakpoint. I had suggested rewriting the source code, which was awe-inspiringly prescient, because that’s what I’m going to do now. Here’s the original code:

8	if (ShouldReturn())
9		return;

And here’s the revised code:

8	int localVar = ShouldReturn();
9	if (localVar)
10		return;

The return at line 10 will still be optimized away. However, the revised code allows us to set a simple breakpoint at line 9 that will stop when we want:

(gdb) break 9 if localVar != 0

No knowledge of the architecture, machine registers, or assembly language is required.

From the beginning of time (January 1970, of course), programmers have struggled over coding style. Objective-C programmers, for example, expend undue effort arranging their brackets. (I have [NSMutableArray array] going to the Final Four.) For some, bracket-making becomes a kind of game or contest.

[[[[[[[[[[[[[See how] many] method] calls] we] can] fit] on] one] line] of] source] code];

I’ve changed my coding style over the years, but I’ve settled on one fundamental principle: write your code so that it’s easy to debug. All your fancy margin-aligning isn’t going to help when you need to figure out why your app keeps exploding. If you have nested method calls on one line of code, you can’t easily set a breakpoint in the middle. That’s why I prefer as much as possible to have only one method call per line of code, and create a local variable to store the return value.

There is a misconception that local variables are expensive, in terms of either computation or memory. The truth is that local variables are very cheap, the value meals of the computing world. (Would you like trans fat with your saturated fat?) It only takes one machine instruction to store a pointer address to a local variable. One machine instruction is really quite fast, about as fast as you can get — at least with restrictor plates. With regard to memory, local variables only take up stack space. To create a local variable, you simply move the stack a little. When the method or function returns, the stack is moved back, and thereby the space reserved for local variables is automatically recovered. Of course, you don’t want to create large C arrays on the stack, but a pointer to an Objective-C object only takes 4 bytes on the stack for 32-bit, 8 bytes for 64-bit. The default 32-bit stack size is 8MB, so you’re not going to run out of space unless you have deeply recursive calls.

Even these small costs are only relevant in the context of your app’s unoptimized, debug configuration. For your customers, on the other hand, local variables are free. As in Mumia, or Bird. When you compile your app using the release configuration, the local variables disappear, the compiler optimizes them away. (By the way, this is one of the reasons that debugging the release build of your app can be a frustrating and/or wacky experience.) To see the optimization in action, let’s consider some sample code:

1  #import <Foundation/Foundation.h>
2
3  @interface MyObject : NSObject {}
4  @end
5
6  @implementation MyObject
7
8  -(NSString *)myDirectProcessName {
9  	return [[[NSProcessInfo processInfo] processName] lowercaseString];
10 }
11
12 -(NSString *)myRoundaboutProcessName {
13 	NSString *myRoundaboutProcessName = nil;
14 	NSProcessInfo *processInfo = [NSProcessInfo processInfo];
15 	NSString *processName = [processInfo processName];
16 	NSString *lowercaseString = [processName lowercaseString];
17 	myRoundaboutProcessName = lowercaseString;
18 	return myRoundaboutProcessName;
19 }
20
21 @end
22
23 int main(int argc, const char *argv[]) {
24 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
25 	MyObject *myObject = [[[MyObject alloc] init] autorelease];
26 	NSLog(@"My direct process name: %@", [myObject myDirectProcessName]);
27 	NSLog(@"My roundabout process name: %@", [myObject myRoundaboutProcessName]);
28 	[pool release];
29 	return 0;
30 }

The above code is obviously contrived and useless. It only has value for explanatory purposes, and perhaps in the app store for $0.99. The methods -myRoundaboutProcessName and -myDirectProcessName do the same thing, the former with and the latter without local variables. Here’s the i386 disassembly for the methods when compiled using the debug configuration:

-[MyObject myDirectProcessName]:
00001d2a	nop
00001d2b	nop
00001d2c	nop
00001d2d	nop
00001d2e	nop
00001d2f	nop
00001d30	pushl	%ebp
00001d31	movl	%esp,%ebp
00001d33	pushl	%ebx
00001d34	subl	$0x14,%esp
00001d37	calll	0x00001d3c
00001d3c	popl	%ebx
00001d3d	leal	0x000012e8(%ebx),%eax
00001d43	movl	(%eax),%eax
00001d45	movl	%eax,%edx
00001d47	leal	0x000012e4(%ebx),%eax
00001d4d	movl	(%eax),%eax
00001d4f	movl	%eax,0x04(%esp)
00001d53	movl	%edx,(%esp)
00001d56	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d5b	movl	%eax,%edx
00001d5d	leal	0x000012e0(%ebx),%eax
00001d63	movl	(%eax),%eax
00001d65	movl	%eax,0x04(%esp)
00001d69	movl	%edx,(%esp)
00001d6c	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d71	movl	%eax,%edx
00001d73	leal	0x000012dc(%ebx),%eax
00001d79	movl	(%eax),%eax
00001d7b	movl	%eax,0x04(%esp)
00001d7f	movl	%edx,(%esp)
00001d82	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d87	addl	$0x14,%esp
00001d8a	popl	%ebx
00001d8b	leave
00001d8c	ret
-[MyObject myRoundaboutProcessName]:
00001d8d	nop
00001d8e	nop
00001d8f	nop
00001d90	nop
00001d91	nop
00001d92	nop
00001d93	pushl	%ebp
00001d94	movl	%esp,%ebp
00001d96	pushl	%ebx
00001d97	subl	$0x24,%esp
00001d9a	calll	0x00001d9f
00001d9f	popl	%ebx
00001da0	movl	$0x00000000,0xe8(%ebp)
00001da7	leal	0x00001285(%ebx),%eax
00001dad	movl	(%eax),%eax
00001daf	movl	%eax,%edx
00001db1	leal	0x00001281(%ebx),%eax
00001db7	movl	(%eax),%eax
00001db9	movl	%eax,0x04(%esp)
00001dbd	movl	%edx,(%esp)
00001dc0	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx
00001dcb	leal	0x0000127d(%ebx),%eax
00001dd1	movl	(%eax),%eax
00001dd3	movl	%eax,0x04(%esp)
00001dd7	movl	%edx,(%esp)
00001dda	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001ddf	movl	%eax,0xf0(%ebp)
00001de2	movl	0xf0(%ebp),%edx
00001de5	leal	0x00001279(%ebx),%eax
00001deb	movl	(%eax),%eax
00001ded	movl	%eax,0x04(%esp)
00001df1	movl	%edx,(%esp)
00001df4	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax
00001e05	addl	$0x24,%esp
00001e08	popl	%ebx
00001e09	leave
00001e0a	ret

As expected, -myRoundaboutProcessName makes more room on the stack than -myDirectProcessName:

00001d34	subl	$0x14,%esp
00001d97	subl	$0x24,%esp

At 00001da0, -myRoundaboutProcessName sets the value of the local variable to nil, as in line 13 of the source code. The interesting differences, though, are immediately after the calls to objc_msgSend(). By the standard ABI, the register eax contains the return value of objc_msgSend(). In -myDirectProcessName, the value in eax is simply moved to the register edx:

00001d5b	movl	%eax,%edx

In contrast, -myRoundaboutProcessName first stores the value on the stack before moving it to edx. The address on the stack is the space reserved for the local variable:

00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx

After the final objc_msgSend() call, -myDirectProcessName doesn’t bother to do much, because the return value in eax will become the return value of the whole method. In -myRoundaboutProcessName, it needs to store values in local variables as in lines 16 and 17 of the source code:

00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax

So that’s how the methods differ in the unoptimized build. Now let’s see what happens when we use the release configuration. Here’s the optimized disassembly for -myDirectProcessName:

-[MyObject myDirectProcessName]:
00001dce	pushl	%ebp
00001dcf	movl	%esp,%ebp
00001dd1	subl	$0x18,%esp
00001dd4	movl	0x00003000,%eax
00001dd9	movl	%eax,0x04(%esp)
00001ddd	movl	0x0000302c,%eax
00001de2	movl	%eax,(%esp)
00001de5	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dea	movl	0x00003004,%edx
00001df0	movl	%edx,0x04(%esp)
00001df4	movl	%eax,(%esp)
00001df7	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dfc	movl	0x00003008,%edx
00001e02	movl	%edx,0x0c(%ebp)
00001e05	movl	%eax,0x08(%ebp)
00001e08	leave
00001e09	jmpl	0x0000400a	; symbol stub for: _objc_msgSend

The optimized method is significantly shorter, as expected from the compiler option -Os. First, you’ll notice that all those pesky nop instructions have been deleted. Stallman put them in unoptimized builds just to annoy us. (Or they may have been for Fix and Continue, but I always assume the worst.) There are additional optimizations as well that I won’t belabor here, because I’m eager to get to the climax. (Sorry, dear.) For your enlightenment and enjoyment, here’s the optimized disassembly for -myRoundaboutProcessName:

-[MyObject myRoundaboutProcessName]:
00001e0e	pushl	%ebp
00001e0f	movl	%esp,%ebp
00001e11	subl	$0x18,%esp
00001e14	movl	0x00003000,%eax
00001e19	movl	%eax,0x04(%esp)
00001e1d	movl	0x0000302c,%eax
00001e22	movl	%eax,(%esp)
00001e25	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e2a	movl	0x00003004,%edx
00001e30	movl	%edx,0x04(%esp)
00001e34	movl	%eax,(%esp)
00001e37	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e3c	movl	0x00003008,%edx
00001e42	movl	%edx,0x0c(%ebp)
00001e45	movl	%eax,0x08(%ebp)
00001e48	leave
00001e49	jmpl	0x0000400a	; symbol stub for: _objc_msgSend

Identical! Ah, that’s nice. Smoke ‘em if you got ‘em.

In conclusion, feel free to sprinkle, pepper, dash, or even drown your code with local variables. And with the engineering hours of debugging time you save, get me a nice (not free) present. I’m partial to flavored coffee and unflavored MacBooks.

Vienna 2.3.2 security update

Monday, July 27th, 2009

I’ve just released the security update Vienna 2.3.2. Security is no laughing matter, so all Vienna users need to update to version 2.3.2. Release notes are here.

The technical details of the vulnerability are instructive. The problem was this line of code:

returnCode = NSRunAlertPanel(alertTitle, alertBody, NSLocalizedString(@"Delete", nil), NSLocalizedString(@"Cancel", nil), nil);

Well, that’s not entirely true. The problem was that line combined with certain assignments of alertBody, for example:

alertBody = [NSString stringWithFormat:@"Are you sure you want to unsubscribe from \"%@\"? This operation will delete all cached articles.", [folder name]];

By default, the value of [folder name] comes from a most trustworthy source: the internet. If, by chance or design, the name contains conversion specifiers, you’re in big trouble. For a discussion of this kind of vulnerability, see Apple’s Secure Coding Guide. The Secure Coding Guide is a must read for Mac Developers. (I also recommend this. Tell them Jeff sent you.)

One simple fix for the vulnerability is as follows:

returnCode = NSRunAlertPanel(alertTitle, @"%@", NSLocalizedString(@"Delete", nil), NSLocalizedString(@"Cancel", nil), nil, alertBody);

With this fix, the alertBody string — which contains the unvalidated folder name — is no longer treated as a format string.

You need to be very careful about using NSRunAlertPanel() and similar functions. Unfortunately, the API and documentation are quite bad. The Discussion doesn’t even mention that the second argument is a format string; for that, you have to read the documentation for another function. Furthermore, it’s absolutely insane to put three unrelated items between the format string and the optional arguments for the format string.

As you know, however, you go to war with the API you have, not the API you might want or wish to have at a later time.

Radar Bug Friday: Bonus Edition

Friday, March 13th, 2009

Though Apple Bug Friday has gone the way of Rip and Robert Van Winkle, I’m bringing it back for one last, glorious, remunerative show. Opening act: some weird dancing fool who people last admired in the 80s. Take your choice.

The continuing failure of Apple to provide a searchable bug database has forced developers to take matters into their own hands. Witness Open Radar. I’ve never been one to join any club that would have me as a member — nor have I been one to be invited to any clubs — so I’ve decided to host my own list of bugs. Keep in mind that these are not all of the Radar bugs I’ve ever filed. They’re not even all of my currently unfixed bugs. They are, however, a very large subset of my currently unfixed bugs. (I’d say the cardinality is somewhere between aleph-null and aleph-one.)

For legal, moral, and comical reasons, I’ve edited the bugs to remove confidential information. I’ve also left out the embarrassing nude photos I attach for ADC. (I would stop uploading them, but engineering always requests more.) I hope that you find these bugs useful and also enjoy reading them as much as I enjoyed writing them. In fact, it’s a metaphysical certainty that you will enjoy reading them at least that much.

After imparting this vital information, I leave you to go in, through, and beyond … for coffee and a donut.

dSYM in your bundle or just happy to see me

Tuesday, January 20th, 2009

It’s been a while since I posted last. Rest assured that I did survive the Y2K9 disaster, though not unscathed. Since bloggers and other entertainers — such as Brian Williams — are required by law to offer a retrospective at the end of a year, I’ve been scanning the Top 10 lists of Top 10 lists of things that we have gained and lost in 2008. Next to our collective sanity, the most significant loss of the year was STABS. Actually, it wasn’t so much lost as deprecated. This means that we can’t expect any new features (or bugs!), and support for STABS debugging symbols may disappear in some future operating system, say, Windows -400. (I assume that the countdown of version numbers from 95 to 7 is intended to accurately represent the software’s regression.) In the transition from STABS to DWARF, it was thought (by the people who matter, viz., me) that we also lost the ability to ship debugging symbols with our apps. Luckily, it was discovered (again, by the people who matter) that we did not lose this ability.

Developers sometimes need to give users a debug version of an application. For example, a user may be experiencing an exception or crash that the developer cannot reproduce. Including debugging symbols with the app allows the reports to be fully symbolized. With STABS, the symbols reside within the app’s executable, so shipping them is trivial. The DWARF with dSYM format, on the other hand, puts the debugging symbols in a separate file. (To be accurate, a separate file within a separate bundle, but we’ll ignore that fact for this sentence.) By default, Xcode creates MyApp.app.dSYM in the same folder as MyApp.app, and indeed, Leopard’s crash reporter can locate MyApp.app.dSYM in the same folder as MyApp.app regardless of which folder they’re in on disk. Theoretically, then, you could have the user put a .dSYM in the same folder as the app. However, making the user do this would be, in a word, lame. In two words, pretty lame. Moreover, it doesn’t work at all on Tiger. Pretty, pretty lame.

When I face an insoluble problem, my tendency is to step back and get philosophical. Why do I exist? Why does the universe hate me? Who was the real Darrin? More to the point: what is an app? Essentially, an app is a command-line tool in a box with a pretty bow. (Another iSweater, just what I needed!) An app’s main executable file is located in the directory Contents/MacOS of the .app bundle. You can even launch an app from the command line, e.g.,

/Applications/Safari.app/Contents/MacOS/Safari

assuming that you haven’t deleted Safari for security reasons. So how does this information help us? It doesn’t — I’m just killing time here. However, it’s worth noting that if you build the Release configuration of a command-line tool project, Xcode by default creates MyTool.dSYM in the same folder as MyTool. In both Leopard and Tiger, the crash reporter can locate the .dSYM there. Thus, you would expect that the crash reporter can also locate MyApp.app/Contents/MacOS/MyApp.dSYM when your app crashes. And you would be right! (Of course, you would expect this because I just told you, whereas originally you would have expected to try a bunch of stuff and fail, like putting MyApp.app.dSYM in MyApp.app/Contents/MacOS.)

The beauty of this technique is that it works not only for the app’s main executable but also for other embedded executables such dynamic libraries and frameworks. When a crash occurs involving MyFramework.framework, the crash reporter will find

MyApp.app/Contents/Frameworks/MyFramework.framework/Versions/A/MyFramework.dSYM

You can build the framework in a separate Xcode project and copy the product along with its embedded dSYM into your app’s bundle, and the symbols will be found at crashtime. (That’s runtime with a bang.) In Tiger, the line numbers of the source code files can sometimes be a little off in the crash reports; this may be due to bugs in the handling of stripped binaries by atos, which I mentioned in my earlier post.

Now that we know where to put the debugging symbols in the app bundle, how do we get them there? Manual copying is unthinkable (like giving David Pogue a copy of OS X GM before ADC members, or putting Leon Panetta in charge of the CIA). If your entire build process is not automated, you should give up software development immediately and look for another career; I recommend professional ice dancing. You could write a shell script to copy MyApp.app.dSYM from the build directory, but that’s only slightly less annoying than having your users copy it to /Applications, because it’s something that Xcode should do itself.

Fortunately, the Xcode build setting reference tells us how to configure this. Or so one would think. Well, at least the relevant build settings are found in the environment variables … after you’ve written your shell script. The Xcode build transcript normally doesn’t show environment variables, but you can add a run script build phase to your target and check the option “Show environment variables in build log”. The environment variables reveal the default values for DWARF_DSYM_FOLDER_PATH and DWARF_DSYM_FILE_NAME, which Xcode uses in creating the dSYM file. Although you won’t find them in the target’s list of build settings, you can create them yourself in the User-Defined section. To embed the dSYM within the app bundle, just set DWARF_DSYM_FOLDER_PATH to $(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_FOLDER_PATH) and DWARF_DSYM_FILE_NAME to $(EXECUTABLE_NAME).dSYM. These settings should work for both apps and frameworks.

My beard has grown longer over the course of this post, and my knees are starting to ache, so it’s time to wrap it up, tip my hat to the new year, and meet the new boss.

Working without a nib, Part 7: The empire strikes back

Monday, October 20th, 2008

I recently received an email from Steve Jobs. He told me that he has read all of my blog posts. I found this ridiculous, so I replied, “You’re full of it!” Shockingly, he wrote back. “Actually,” he said, “all of the new apps of the past few years are nibless.” He then described the Apple-sanctioned technique of writing a nibless application.

In previous posts of this series, I was forced to use obscure workarounds such as +[NSObject poseAsClass:] and method_exchangeImplementations() to prevent NSApplicationMain() from trying to load a nib. Jordy Rose of Belkadan Software suggested to me that the easiest way to prevent NSApplicationMain() from trying to load a nib would be to avoid calling NSApplicationMain() at all. Instead, you would just call -[NSApplication run]. However, I was worried that NSApplicationMain() might do something else important (like phone home to get permission to launch). We don’t know, because we don’t have the source code.

I’m still waiting for the source code, but Steve did indicate that it was safe to skip NSApplicationMain(). Thus, there’s no need for tricksy runtime manipulation, except perhaps to prove your mad programming skills. Mad, that is, in the sense of having worn one ring for too long. Steve also affirmed that our call to -[NSApplication setAppleMenu:], a method that Apple removed from the API, will continue to work until there’s a replacement API. Indeed, Apple is working on some kind of replacement. Excellent! /me plays air guitar. So if you’re counting at home, it’s now (1) Exchange support and (2) Nibless support.

He didn’t mention the Open Recent menu, so it appears that my ugly hack is still required for non-document-based apps. This cannot be considered part of the Apple-sanctioned technique, though it is sanctioned by me, which is more desirable, because my approval comes in seven delicious flavors. I’ve updated Nibless for Leopard in light of Steve’s revelations and am making version 2.0 available for immediate download. This is major progress, because it already puts me two versions ahead of VLC.

So long, Steve, and thanks for all the fish!

Cocoa memory management for smarties, Part 3: accessors

Sunday, October 5th, 2008

This post is not over until Minnesota Fats says it is, or until I figure out why he played in Iowa. In the meantime, let’s talk about getters and setters. A setter is distinguished by the prefix set, while a getter is distinguished by not having the prefix get. Got it? Getters and setters come in pairs; collectively, they are commonly known as accessor methods, less commonly as American Airlines or pocket rockets.

Accessor methods provide encapsulated access to an object’s instance variablesinstancvariabls for short. If your instancvariabl is an Objective-C object (in Freudian terms, an id), your accessors should almost always look like this:

-(id) thing
{
	return [[_thing retain] autorelease];
}

-(void) setThing:(id)aThing
{
	if (_thing != aThing)
	{
		[_thing release];
		_thing = [aThing retain];
	}
}

You can copy rather than retain in setThing: if the class of aThing conforms to <NSCopying>.

It may seem redundant to [[_thing retain] autorelease] in thing, but this has an important purpose. (A special purpose, if you will, which you’ll want to use every chance you get.) Simply returning _thing in thing does not suffice, as the following code demonstrates.

1 id something = [myObject thing];
2 [myObject setThing:nil]; // Clear out the thing.
3 [something doSomething];

In line 3, your app will go boom. I’ve seen it argued (on bathroom stalls, for example) that the caller ought to [[myObject thing] retain] on line 1 because of this possibility, but that’s simply not the caller’s responsibility. It’s an implementation detail of myObject that thing returns an instancvariabl which setThing: releases. Moreover, the caller could inadvertently call setThing: by calling some other method that calls setThing: rather than by calling setThing: directly. With [[_thing retain] autorelease], thing ensures that _thing will not be deallocated in the caller’s scope (as long as the caller doesn’t do anything stupid, like call out Minnesota Fats).

No discussion of accessors is complete without talking about thread safety. In order to write thread-safe accessors, you should first drink a bottle of whiskey. This is required because writing thread-safe accessors is a fool’s errand. Don’t do it. Don’t try it. Don’t even think about. Redesign your code to avoid the need for thread-safe accessors. If you feel you must, however, then you should acquaint yourself with Technical Note TN2123, because you’ll find yourself referring to it frequently. It’ll be like a nightmare, just getting (and setting) worse and worse. But at least the salad dressing will be good.

Cocoa memory management for smarties, Part 2: working with a nib

Monday, August 25th, 2008

Well, it’s back to work for the moment. I was offered the VP slot, but I had to turn it down because they refused to accept the NDA: my name would be on the ticket, but nobody could talk about me publicly. Also, they wouldn’t get rid of the brown M&Ms. I am waiting for an offer by email from another guy, but I don’t hold out much hope, since I’m also waiting for him to learn how to use email.

In part 1 of this series I talked about Cocoa memory management. For a summary of that post, load it in your browser, select all, and pass it to the Summarize system service. And now for something completely the same, in part 2 I’m going to talk about Cocoa memory management. As you all know, I love nibs. I find them to be a very human-friendly interface, much like the gom jabbar. A nib file contains archived Objective-C objects that are instantiated when the nib is instantiated, so their memory needs to be managed just as objects instantiated programmatically. Fortunately, if the nib File’s Owner is an NSWindowController or (new in Leopard) NSViewController, the File’s Owner takes care of the memory management automatically. Otherwise, it’s your job.

According to the documentation, Objects in a nib file are initially created with a retain count of 1. As it rebuilds the object hierarchy, however, Cocoa autoreleases any objects that have a parent or owning object, such as views nested inside view hierarchies. By the time the nib-loading code is complete, only the top-level objects in the nib file have a positive retain count and no owning object. This makes the top-level objects a potential memory leak if your code does not assume responsibility for them. If you look at your nib with the icon view in Interface Builder, the top-level objects consist of everything except the proxy objects such as File’s Owner, First Responder, and (new in Leopard) Application. Thus, top-level objects include not only windows but also contextual menus, array controllers, etc. For example, Vienna’s MainMenu.nib, pictured below, contains 14 top-level objects. (Kids, don’t try this at home.)

MainMenu.nib

By the way, the MainMenu.nib file, or whatever is specified by NSMainNibFile in Info.plist, is actually a special case, because the File’s Owner is NSApplication (or whatever is specified by NSPrincipalClass). You don’t need to worry about its memory management either, although the objects in the nib will usually remain in memory for the lifetime of the app. On termination, NSApplication may or may not choose to release those objects, as it pleases, so don’t depend on any code in dealloc. Cleaning up memory on app termination is like sweeping your floor right before a tornado hits.

In order to manage the memory of the nib’s top-level objects, you’ll need a reference to them. The method -[NSNib instantiateNibWithOwner: topLevelObjects:] is handy in this respect. The documentation of the (NSArray **) topLevelObjects argument may be a little confusing, though: On input, a variable capable of holding an NSArray object. On output, this variable contains an autoreleased NSArray object containing the top-level objects from the nib file. Although the NSArray is autoreleased, the top-level objects themselves are not. Each object in the NSArray will have a retain count of at least 2. Thus, after the NSArray is deallocated and releases its objects, they will each still have a retain count of at least 1. A typical way to handle this is as follows.


@interface MyObject : NSObject
{ NSArray * _topLevelObjects; }
@end

@implementation MyObject

-(id) init
{
	self = [super init];
	if (self)
	{
		NSNib * nib = [[[NSNib alloc] initWithNibNamed:@"MyNib" bundle:nil] autorelease];
		if (nib && [nib instantiateNibWithOwner:self topLevelObjects:&_topLevelObjects])
		{
			// The array is autoreleased, so we need to retain it.
			// Release the objects now so that they'll be deallocated along with the array.
			[[_topLevelObjects retain] makeObjectsPerformSelector:@selector(release)];
		}
	}
	return self;
}

-(void) dealloc
{
	[_topLevelObjects release];
	[super dealloc];
}

@end

If you use a different method to instantiate the nib, such as +[NSBundle loadNibNamed:owner:], or if you don’t want to keep an array of the top-level objects, you’ll need to create an IBOutlet for each top-level object in the nib and release the objects in dealloc along with any other ivars.

A major caveat for do-it-yourself nib loading is that even if you take care to release your top-level objects, you can still get a memory leak if you use Cocoa bindings in your nib. This occurs when an object in the nib binds to the File’s Owner. The File’s Owner never gets deallocated in this case, and thus neither do the top-level objects. Again, NSWindowController and NSViewController already take care of this problem automatically, but you’ll need to deal with it if the File’s Owner is not one of those classes or their subclasses. Basically, there are three possible solutions:

  1. Unbind the bindings programmatically. You’ll need to do this elsewhere than the File Owner’s dealloc, which won’t get called unless you unbind.
  2. Bind to a different object in the nib rather than to the File’s Owner.
  3. Don’t use Cocoa bindings and nibs, because they’re teh suck.

Cocoa memory management for smarties, Part 1: release me not

Monday, July 28th, 2008

If I were to offer one bit of advice to young programmers, I would say always … uh … never … forget to check your references. Not to mention your optics and your closet. Supposedly, the rules of Cocoa memory management are simple. Just set GCC_ENABLE_OBJC_GC = required and wait for the money to roll in, right? For those who don’t enjoy the smell of Objective-C garbage collection, we have the tried-and-true manual rules. If you obtain a reference to an object by calling +alloc, +allocWithZone:, +new, or -copy, then the reference should remain valid indefinitely. A reference obtained in any other way is guaranteed to be valid only during the current event loop, so you’ll need to call -retain if the reference should remain valid for longer (e.g., if you want to store it in an instance variable). Each of the aforementioned method calls must be balanced at some point by a call to -release, if it’s safe for the object to deallocate immediately, or -autorelease, if you need the object to stick around until the end of the event loop.

In theory, these rules are foolproof. In practice, however, they’re not even expert-proof, as we see when the experts’ apps unexpectedly quit. Thus, I believe that writing a series of posts exploring the subtleties of Cocoa memory management is a worthy endeavor. Moreover, I need to kill some time before football season kicks off.

Below is a simplified version of a crasher I once discovered and fixed. I’m only giving the bare essentials here. There was a lot of code involved, which made it more difficult to diagnose.


@implementation MYChild

-(void)doSomething
{
	...
	if (...)
	{
		[self setMyProperty:theValue];
	}
	if ([_myArray count] > 0)
	...
}

@end

@implementation MYParent

-(void)start
{
	...
	_myChild = [[MYChild alloc] init];
	[_myChild addObserver:self forKeyPath:@"myProperty" options:0 context:NULL];
	...
}

-(void)stop
{
	...
	[_myChild removeObserver:self forKeyPath:@"myProperty"];
	[_myChild release];
	_myChild = nil;
	...
}

-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
	...
	[self stop];
	...
}

@end

Can you guess where the crash occurred? The particularly tricky part is that observeValueForKeyPath: is called by the Cocoa runtime rather than by the app’s own code, which means that if you’re not already aware of the key-value observer, the cause of the crash is rather mystifying.

My solution, by the way, was to replace [_myChild release] with [_myChild autorelease]. Ideally, you would probably access all ivars via getters and setters, but that’s a topic for a later post in this series.

In light of the above example, let’s now attempt to add to the rules of memory management:

  • Rules are for suckers, simpletons, and lawyers.
  • No method is safe, only call paths are safe. You can’t look at a method in isolation and determine that it handles memory management properly. On the other hand, if you’re doing object-oriented encapsulation correctly, you shouldn’t have to worry about the implementation of other objects. The proper unit of analysis, I believe, is the entire call path within the object’s implementation.
  • Don’t release any method arguments. It’s almost always possible for the caller of aMethod: to have code such as { [anObject aMethod:anArgument]; [anArgument anotherMethod]; }, so don’t invalidate anArgument during the current event loop. This is the reason why -autorelease was invented. (Also, it would be pretty silly to have NSAutoreleasePool without anything to go in the pool.

As for the jello, I can explain. I was hungry and preparing for gravity to reverse.

Compiler indirectives and metaphorical keypaths

Wednesday, July 2nd, 2008

I am, like, literally ROTFRTFMIMHOLOLYMMVIIRCFUBAROTOH!

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(likeNoWay:) name:@"ROTFRTFMIMHOLOLYMMVIIRCFUBAROTOH" object:nil];

A string literal is a sequence of characters enclosed in quote-unquote ‘\”quotation marks\”‘ (wiggles index and middle fingers). In the C programming language — so-named because its inventors lacked imagination — a string literal represents an array of char terminated by a null (or by a comma when the feeling’s not that strong). In the Objective-C programming language — otherwise known as The O.C. — a string literal in a compiler directive (e.g., @"NSBirdJustFlewIntoMyWindowException") defines a constant NSString object.

Chris Hanson and Sanjay Samani have offered some excellent advice about avoiding the use of these NSString literals in your method calls. The problem is that the compiler will accept pretty much any directive: with Objective-C 1.0 the compiler only warns about non-ASCII characters, and with Objective-C 2.0 it doesn’t even do that (for better or worse, but that’s a subject for a different post). Thus, if you happen to misspell a notification name, you’ll never know until your app misbehaves at runtime. I’m sorry, Dave, I’m afraid I can’t do that.

I recommend replacing NSString literals with macros or constant variables (huh?) wherever spelling matters. (Spelling matters everywhere. I’ve seen some pretty bad method names.) Here’s a little trick for handling arbitrary keypaths:

#define KEY1 @"key1"
#define KEY2 @"key2"
#define KEY3 @"key3"
#define DOT @"."

[self valueForKeyPath:KEY1 DOT KEY2 DOT KEY3];

Unfortunately, we’re still at the mercy of misspellings in nib bindings. Yet another reason to do without nibs. But that’s also a subject for a different post and horse of a different color (dried poop).

I’m a Rogue

Saturday, June 7th, 2008

I’m delighted to announce that I’ve been hired as Rogue Amoeba Employee 008. (Employee 007 is Longwell, Justin Longwell.) I’ll be working on Rogue Amoeba’s apps as a software engineer, as well as providing comic relief. Thanks to Alex, Paul, and Quentin for this once-in-a-lifetime opportunity. My coworkers at Rogue Amoeba Software, LLC include fellow bloggers Mike Ash and Guy English. Together we form perhaps the most talented group of Mac programmers outside of Apple. (Yes, I’m talking to you, Adobe and Microsoft.) Indeed, I haven’t seen such an array of stars in one place since the Ghostbusters music video.

I’ve joined Rogue Amoeba just in time to miss WWDC ’08. In an earlier post, I mentioned that I left Marko Karppinen & Co. LLC just in time to miss WWDC ’07. (I’ll make it there someday! :-( ) The question is, what have I been doing in the meantime? Backpacking through Europe? Rotting in Gitmo? Running for President? No, I’ve been working on super-secret projects for Francis Technical Services, LLC. Although FTS is a little-known company, its software is well-known, at least inside Apple. For example, FTS is responsible for Radar, the Apple-internal Cocoa app that opens when employees click on the funny-looking rdar://problem/ URLs you often see. The number at the end of the URL corresponds to the problem ID of the bugs you file on the web with Apple Bug Reporter (also known as RadarWeb). It’s useful to have one of these URLs available when communicating with Apple engineers, because their managers forbid them from lifting a finger without a Radar problem number.

While at FTS, I did some bug analysis for Radar: that is, analysis of bugs in Radar’s source code, not analysis of bugs in Radar’s database, to which as a contractor I had extremely limited access. However, I mostly worked on other projects, such as Radar’s sibling app Sonar. Apple’s DTS uses Sonar to communicate with ADC members (such as me!). If you’ve ever seen sonr://request/ URLs — the Mighty Quinn provides one in an Apple Mailing List post — those open in Sonar. (By the way, I’ve read all of your emails to DTS. No, you haven’t been accepted to the iPhone Developer Program. Stop whining.) Another project that I worked on was Merlin, an app for Apple’s Human Resources department. I think that Steve Jobs is doing an outstanding job at Apple, so before I left I used Merlin to double his annual salary.

I’d like to thank Dave Francis, the owner and founder of Francis Tech, for the opportunity to work there. Despite appearances, I’m not really the type to hop from job to job. I’ve just been looking for Mr. Right, LLC, a company to fall in love with, marry, and have baby apps with. (As for any other support claims, sorry Billie Jean.) I can see myself growing old with Rogue Amoeba. (Easy to see when you’re already old?) Besides, someone has to stick around and keep an eye on mikeash, stop him from taking over the world.

Now without further ado, please take out your credit card and buy some of our fine software. As a special bonus for our Leopard customers, your purchase will be (code-)signed by your favorite Rogue Amoeba star.