I just fixed a rather stupid bug that had been bugging me for hours. This is rather Cocoa and Objective C specific, so bear with me.

My application has an NSTableView—a widget to display a table of data. An NSTableView doesn’t store its own data; rather, it has a delegate object called a data source that supplies data to it. In order for the NSTableView to get its data in a well defined manner, it requires that its data source implement two methods: numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:. These two methods return the number of rows in the table and the value at a particular column/row, respectively. Side note: why does the NSTableView need to query for the number of rows, but not the number of columns? Because it’s extremely poorly designed, that’s why.

Anyway, normally the way one would require that an object implement some method is via a “protocol.” A protocol in Objective C is what Java calls an interface, basically just some static guarantee that an object implements something. Using a protocol allows fun stuff, like the ability to verify at compile-time that an object will implement some method.

However, for reasons that are not clear to me, NeXT/Apple did not make the data source into a protocol. They made it what they call an “informal protocol,” which basically means that things will be checked at run-time, not at compile-time or link-time. Another side note: being C based, you might expect that Objective C would be a static language. Quite the opposite. It’s very common to, for example, add methods to an object or class at run-time.

Anyway, I got my program all working perfectly. Until I tried to build a “release” version of it. I narrowed it down to: if I compile it with -O0, it works perfectly; if I compile it with -O1 or higher, suddenly NSTableView doesn’t think that my data source responds to the appropriate methods. Since NSTableView doesn’t trust my object, it doesn’t display any data.

After an hour or so of trying to narrow down the bug, I finally found and fixed it. It cropped up out of my stupidity more than anything else—of course—but some stronger typing would have helped things out a little more.

The object model of Objective C, as I said before, is entirely dynamic, modelled on Smalltalk. A side-effect of this is that the object model of Objective C is not statically typed. I’m lying a little bit, but only a little bit. So the “C” in Objective C is statically weakly typed, and the “Object” part in Objective C is dynamically weakly typed.

The problem came when I wrote the “init” method for my data source. In NeXT/Apple’s Objective C framework, creation of an object happens in two stages. The “alloc” and friends methods deal with memory allocation, and the “init” and friends methods deal with object initialization. This is kind of handy because it allows memory allocation and object initialization to be independent and orthagonal. You can do stuff like: [[Foo allocWithZone: [self zone]] initWithSize: 5], using a combination of allocator and initializer that the class creator might not have thought of.

Anyway, because I’m an idiot, for some reason when I was writing my class’ initializer, I thought that “init” didn’t have a return value. So I went on my merry way and wrote:

- (void)init
{
  [super init];
  size = 0;
  /* other initializations ... */
}

That (void) you see before “init” is the return value and comes into play only now and then and even then only sort of. Its intention is to deal with C being statically typed. Another diversion: in Objective C, every message of the same name has the same type signature. So init in one method has the same type signature as init in another. Thus, any code actually calling my init method is none the wiser that I’ve erroneously assumed it has no return value.

So calling code calls init and looks in register eax (on Intel, or r3 on PowerPC) and assumes that that’s the return value. By coincidence, it happened to work out perfectly for -O0 but not for -O1 or higher. So, after changing the method to:

- init
{
  self = [super init]; /* yes, I know this is weird. it works in Objective C */
  if (self) {
    size = 0;
    /* ... other initializations */
  }
  return self;
}

Everything worked perfectly. If only there had been some sort of type error to clue me in!

Advertisements