iPhone SDK v2.1 gotcha.

The easiest way to load a NIB containing some element of your user interface on the iPhone is to call the UIKit addition to NSBundle loadNibNamed:owner:options:.

So throughout my code I have the following:

NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MyNIBFile" owner:self options:nil];
UIViewController *c = [nib objectAtIndex:1];

Um, there’s a problem here. On return the NSArray returned contains at index offset 0 a reference to the bundle; the first defined object is at 1 and so forth.

That is, on the iPhone 2.0 SDK.

Using the iPhone 2.1 SDK, the index starts at 0; a reference to the bundle is not returned. Thus, you need to change the statement above to:

UIViewController *c = [nib objectAtIndex:0];

Meh.

Naturally, of course, if I had used the iPhone SDK like the MacOS SDK, I would have set the file owner in the NIB to an object which contains IBOutlets for the items in the NIB being loaded. And had I done this, I wouldn’t have had a problem at all.

A detached table delegate must be referred in the NIB

Here’s a puzzling problem I just encountered a third time, and wasted a better part of the morning sorting out, which I had solved before but forgot to record my solution.

On the iPhone, I wanted to create an object which serves as a delegate to a UITableView. This object is then instantiated in the NIB file alongside the UIViewController which owns the control on which the UITableView lives. So I created an instance, and pointed the delegate and dataSource references of the UITableView at my table controller object.

On startup, my iPhone application crashed.

The solution appears to be creating a reference to my object from the owner of the NIB.

My mental model as to how a NIB is loaded on the phone is that every object is instantiated and initialized, then events are fired to begin populating things like the contents of the table. In other words, objects are loaded then events fired. What appears to be going on, however, is that objects are loaded then initialized in an order-dependent fashion which I don’t completely understand–however, by creating a reference from the owner to the delegate object (which is after the table item), the delegate object and the table object are loaded and initialized in the correct order.

So the lesson appears to have the owner object hold references to both the table (indirectly via the view containing the table) and the delegate object which controls the table.

OpenGL Fog: the z component is at the model transformation.

When dealing with fog, the z distance used to compute the fog density. However, which z?

Turns out, the z depth used is the z depth that is computed after applying the GL_MODELVIEW matrix but before applying the GL_PROJECTION matrix. One result of this is that through the careful manipulation of both matrixes you can create the effect of fog on the valley floor below the viewpoint: fog doesn’t just have to be haze in the distance.

Another gotcha to remember.

For some reason a development profile seems tied to the computer as well as the specified phone on the iPhone. What this means is that when you move the device from one computer to another to work on some software (say, a personal laptop to a personal desktop and back again), you need a way to clear out the development profile before things will work again.

I learned this the hard way yesterday when I blew away the contents of my iPhone, only to have things not work correctly (and deleting all my music at the same time).

Turns out the profile certificates are accessible and can be deleted from the iPhone itself: under “Settings” navigate to “General”, then scroll down to “Profiles”: this will show a list of profiles installed on the device which can then be deleted. Delete your development profile(s) from the device before plugging it into the new machine, and Xcode will load the profile for that platform onto the iPhone and things will just work again.

Keeping track of C++ templates

So I needed to build a complex template. I wanted to build a map which also operated as a cache: as the contents of the map hit a limit on the number of objects in the map, the last referred to objects are automatically erased from the map. (What I’m doing is writing a bit of code to insert itself between a Sqlite database and some accessor code, but the pattern of access would cause a lot of unnecessary ‘select’ statements. Rather than trying to load my database in memory and somehow manage the synchronization from a bunch of places, a database cache seemed to make the most sense for my application.)

I ran into two interesting things which I’m noting here for future reference.

Keep the interior value declarations simple by using the typedef keyword.

At one point I found myself writing the following code:

   std::pair<std::map<Key,std::pair<std::list<Key>::iterator, T> >, bool> ret;

And of course the code didn’t work–which was really irritating, because I was getting all sorts of weird messages that had something to do with whatever it was I was declaring. Arg!

My lesson: break the declarations into class-scoped typedef declarations:

typedef std::list<Key> CacheList;
typedef CacheList::iterator CacheIter;
typedef std::pair<CacheIter,T> MapVal;

typedef std::map<Key,MapVal> Map;
typedef Map::iterator MapIter;

Then the declaration of the return value of a std::map::insert() call becomes easy:

std::pair<MapIter,bool> ret;

Once I did this, I learned my second lesson:

Sometimes you need to declare a type as a typename for some odd reason.

I’m sure there is some C++ template expert out there who will tell me why–and it will be blindingly obvious when I hear the explanation. I’m sure it has something to do with the nature of the declarations and how they are used within a template class declaration. But for now, it seems to me that under some cases you need this ‘typename’ keyword for reasons that aren’t obvious to me–but once they’re there, all is good in the universe.

And for those experts out there, I’m sure you spotted what I screwed up in the above listing right away. For the rest of the class, I needed to insert ‘typename’ for the iterator typedefs:

typedef std::list<Key> CacheList;
typedef typename CacheList::iterator CacheIter;
typedef std::pair<CacheIter,T> MapVal;
		
typedef std::map<Key,MapVal> Map;
typedef typename Map::iterator MapIter;

Without the ‘typename’ declaration, I was getting these mysterious errors:

type std::list<Key, std::allocator<_CharT> >' is not derived from type 'mapcache<Key,T>'

For reference, the class that I put together to do this simple caching scheme is here. Licensed with a BSD-style license in case anyone else finds it useful.