Archive for July, 2008

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.

SECURITY ALERT: check your DNS servers

Tuesday, July 22nd, 2008

The full details of the infamous DNS vulnerability have been inadvertently disclosed. The post was pulled, but I can still read it in my Vienna database.

The vulnerability is bad, folks. I completely agree with the decision to keep the details secret while vendors patch their DNS servers. The secret is out now, though, so there’s no time to wait. Perform the web-based test of your DNS servers, and switch to different servers if the results are POOR. If you need to switch, one decent option is OpenDNS.

Laptop users should be especially careful, because your ISP changes when you take your computer to different locations. Even if you pass the test at home, you may be vulnerable at the coffee shop. I recommend hard-coding DNS server IP addresses in the Network pane of System Preferences. You might also want to check the DNS settings of any routers you use.

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