Getting Loaded

Tuesday, November 28, 2006 at 10:04 AM

Posted by: Dave MacLachlan, Member of Technical Staff, Mac Team

Categories are a really interesting feature of Objective-C, especially for those of us who came from C++. Not only do categories allow you to extend other classes for which you may be lacking the source, but they also give you a really simple way of hiding interfaces from your clients without all the baggage of a pImpl pattern. We use categories a lot here at Google to "enhance" the system frameworks.

We do run into a couple of major problems with categories, though. One issue is about class-specific initialization. Objective-C has traditionally had three solutions for class-specific initialization:
  1. lazy initialization
  2. +initialize
  3. +load
With lazy initialization, we call our initialization routine as necessary to make sure our class-specific stuff is initialized before we use it. This works fine except that in implementing it, we often end up writing code where every method starts with a method call to the initialization check, which is ugly and a potential source of stupid bugs.

+initialize is the "best practice" for initializing class variables. For a standard class, it works great. This technique usually does almost everything we want it to do and has reasonably clear semantics, although the part that says "you could be invoked more than once" is a bit of a pain. The problem with +initialize is that it is virtually useless for categories, in that if I override a class's initialize method in my category, I can't call the original initialize. Also, if I have two categories on a class, and both have initialize methods, it is unclear which one will be called. Even if it works now, there's no guarantee the original framework you're extending won't be "enhanced" and break you in the future when an initialize method is added to the class you extended. So, as far as categories are concerned, +initialize is pretty much useless. Interestingly if you think about it, in some ways +initialize is very much like lazy initialization, with the details hidden under the covers. Basically, you have the runtime doing the check instead of you having to code it.

+load is an interesting option, and probably one of the less understood areas of how the Objective-C runtime actually works. According to the NSObject documentation:
  1. +load is invoked once per class or category.
  2. +load is usually invoked before +initialize, but not always.
  3. You can't be sure your superclasses are loaded.
  4. You can't be sure any other classes are loaded.
  5. You know, you really should be using +initialize.
Item 1 is great, but there are a lot of interesting little caveats that follow it. Basically, the docs say you can't call ANY other classes, including your superclass, safely from within a +load. Looking at the obj-c runtime code (ADC registration required), we can get a bit more information on how +load works. We see that we are guaranteed that our superclasses are "+load"ed before we are "+load"ed, but we can't call out to other classes, so officially we can't use NSDictionary, NSString, et al. In practice, this appears to work pretty much all the time, but I certainly wouldn't intentionally ship code that depended on it.

So it appears that if we stay with traditional Objective-C, we are basically stuck with lazy initialization for doing class-specific initialization in a category. Luckily, if we break with tradition, we can use the "constructor" attribute. Yes, the syntax is ugly, but it does potentially solve a lot of our problems.

Constructors (which is a horrible name that must have been intentionally designed to cause confusion with C++/Java constructors) are guaranteed to be called after +load but before main. This gets rid of several of the caveats of +load, because you can be guaranteed that all your classes are loaded and that +initialize will be invoked on classes as needed by your constructor function.

So doing something like the following gives us a nice, relatively clean way of class-specific initialization in a category.

@implementation Foo(FooBarAdditions)
....
@end

@interface Foo(FooBarAdditionsPrivateMethods)
+ (void)initializeBar;
@end

@implementation Foo(FooBarAdditionsPrivateMethods)
+ (void)initializeBar {
// Initialize stuff here
}
@end

void __attribute__ ((constructor)) InitializeFooBar(void) {
static BOOL wasInitialized = NO;
if (!wasInitialized) {
// safety in case we get called twice.
[Foo initializeBar];
wasInitialized = YES;
}
}

@synchronized swimming Feedback

Thanks to reader Ron Avitzur for pointing out that the nice Objective-C workaround for the DCLP that I showed in a recent post is officially bad. He's got a nice writeup on his blog about why even though it's pretty, it's nothing more than a global flag, which in theory doesn't get us around the problem at all.

Woz comes to Google

Tuesday, November 21, 2006 at 9:21 AM

Posted by: Scott Knaster, Technical Writer

The first time I ever saw the mysterious inscription Woz was while reading the legendary Red Book that came with my Apple ][. I soon found out that Woz was Steve Wozniak, who had designed the Apple ][ and co-founded Apple Computer. The more I learned about Woz and his inventions, the more amazed I was: how he enabled the Apple ][ to use a cheap cassette recorder to load and save data; his incredible design for color output that two professional engineers told me "couldn't possibly work"; his unique disk controller that somehow managed to use software for timing and eliminated the need for all but 5 chips.

Woz is a Silicon Valley legend of the highest order, so we at Google were thrilled to welcome him here last Thursday to talk about his new book, iWoz: From Computer Geek to Cult Icon: How I Invented the Personal Computer, Co-Founded Apple, and Had Fun Doing It. Woz arrived about 20 minutes late, but stayed more than twice as long as he was "supposed" to, answering every question, signing every book, and talking to everyone who wanted to talk to him (and so I figured out why he was late in the first place).

Woz's talk showed why he is an inspiration to engineers (and other people) everywhere. He captivated the crowd with his passion and enthusiasm for what he does, whether it's designing computers, putting on massive concerts, or talking about his life and work. I highly recommend you see Woz on his travels if you get a chance -- if you're lucky, you might even get pranked -- and whether you see him in person or not, his book is a wonderful peek at the life of this remarkable guy.

And now I have this great souvenir:


More Earth for the Mac

Tuesday, November 14, 2006 at 10:49 AM

Posted by: John Gardiner, Technical Writer, Google Earth team, and Rose Yao, Mac Product Manager

Late last week, Google released an updated beta version of Google Earth 4 for Mac OS X (and some other platforms). For those of you who love Google Earth, this release includes a number of neat new features and enhancements:

Performance improvements: we've made 3-D models (such as those created in SketchUp or imported from the 3D warehouse) load more quickly and more realistically.

Paths and polygons: a very cool advanced feature -- and now it's in the FREE version.

Altitude: This is useful for overlays such as weather radar images where you want to see the image from directly above, but also want to look at the ground imagery from an angle. It's also good if you're going on a hike and want to pick an easier path :).

And as usual, we're trying to improve the Google Earth experience with new icons, printing functionality, and more. Check it out!

@synchronized swimming (part 2)

Tuesday, November 07, 2006 at 10:52 AM

Posted by: Dave MacLachlan, Member of Technical Staff, Mac Team

Previously we addressed the problem of optimizing around a shared resource. We came up with one solution, but it was kind of messy, and we wondered if there might be a better way. And now: the conclusion.

There is at least one more elegant solution, but it is slightly less safe. So far I've assumed we're using Objective-C, but what happens if we use Objective C++, specifically Objective C++ with gcc 4? According to the GCC4 porting notes:

GCC 4.0 automatically adds locks around any code that initializes local static variables in C++. If you do not need this protection and want to reduce your code size slightly, you can disable the locking behavior by passing the -fno-threadsafe-statics option to the compiler.

This appears to be backed up by the gcc 4.0 release notes and the C++ ABI. So this implies that if we just change our compiler from standard Obj-C to Obj-C++ we should be able to do the following:

+(id)fooFerBar:(id)bar {
static NSDictionary *foo = [NSDictionary dictionaryWithObjects:...];
return [foo objectWithKey:bar];
}

which is certainly nice and clean. Let's take a quick look at the disassembly:

cxa_guard_acquire

my actual code

cxa_guard_release
cxa_guard_abort
Unwind_Resume
...

and by scanning the code for cxa_* (ADC registration required) we can see that it's doing almost exactly what we want. The only pitfalls here are if we somehow attempt to compile with a gcc version less than 4.0 (we can put in guards against this happening) or we use -fno-threadsafe-statics in a threaded environment, in which case we're asking for trouble, and trouble will certainly follow (we won't do that).

So, we've got a thread-safe shared resource that does what we want with a minimal amount of code. One last tiny issue remains. What happens if we accidentally mix our Objective-C @synchronized with C++ dynamic initialization of local statics?

+(id)fooFerBar:(id)bar {
@synchronized(self) {
static NSDictionary *foo = [NSDictionary dictionaryWithObjects:...];
return [foo objectWithKey:bar];
}
}

and the disassembly shows:

...
objc_sync_enter
objc_exception_try_enter
setjmp
objc_exception_extract
cxa_guard_acquire

my actual code

cxa_guard_release
cxa_guard_abort
objc_exception_try_exit
objc_sync_exit
objc_exception_throw
...

Yes, ladies and gentlemen, you get to pay for 4 lock/unlocks and two
exception stacks to protect your wee shared resource, so you may want
to watch for this pattern in your performance-sensitive code when
porting to Objective C++.

Feedback from part 1

Thanks to reader Bill Bumgarner, who came up with an interesting solution to my problem that has a distinctive Obj-C feel to it:

static NSDictionary *foo = nil;

+(id)fooFerBar:(id)bar {
@synchronized(self) {
if (!foo) foo = [NSDictionary dictionaryWithObjects:...];
ReplaceMethodImplementationWithSelector
([self class], @selector(fooFerBar), @selector(fooFerBar2));
}
return [foo objectWithKey:bar];
}

+(id)fooFerBar2:(id)bar {
return [foo objectWithKey:bar];
}

where ReplaceMethodImplementationWithSelector swizzles fooFerBar with fooFerBar2. So, the first time fooFerBar is called we get our slow case, and any later calls get the fast case, assuming the first thread has completed fooFerBar. This solution provides great performance, and you only have to pay for the synchronize once. Very nice!