Monday, December 1, 2008

Speaking of Errata...

...there's a doozy in Chapter 8, and I don't know how long it will take for the fix to work its way into the source code on the official book site. The problem is easy enough to fix, though, so I thought I'd post it here until the fix is in the official source code.

If you've tried to compile the Chapter 8 Cells project and are compiling against SDK 2.1 or SDK 2.2, then your program is likely crashing with an NSRangeException. Here's what's going on.

Under SDK 2.0, the NSBundle method loadNibNamed:owner:options: returned an array that had, as its first object, a proxy object at index 0. I believe that this proxy object points to File's Owner, but I'm not sure of that, and if anyone knows better, please let me know. Whatever it points to, the existence of this proxy object meant that the first instance in the nib would be located at index 1, so when we grabbed the view in the tableView:cellForRowAtIndexPath: method in the book, we grabbed the object at index 1, which is the first (and only in our case) instance in the nib.

With SDK 2.1+, the behavior was changed so that no proxy object is included in the array. As a result, the index of the first instance in the nib now becomes 0 instead of 1. Since our nib only had one instances (excluding subviews), the array only had one object, meaning that index 1 was now out of range. The fix, is easy - just change
cell = (CustomCell *)[nib objectAtIndex:1];
to
cell = (CustomCell *)[nib objectAtIndex:0];

Here's the full method, using the availability macros to make sure it compiles correctly under all current versions of the SDK:

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCellView"
owner:self options:nil
]
;

// The behavior here changed between SDK 2.0 and 2.1. In 2.1+, loadNibNamed:owner:options: does not
// include an entry in the array for File's Owner. In 2.0, it does. This means that if you're on
// 2.2 or 2.1, you have to grab the object at index 0, but if you're running against SDK 2.0, you
// have to grab the object at index:0.

// You might be tempted to use UIDevice to get the system version number, however that is the
// operating system version, not the version of the SDK that the application is linked against. An
// iPhone operating running iPhone OS 2.2 can, for example, run an application linked against the
// 2.0 vesion of the SDK

// The way to handle this is to use what are called "Availability Macros". The pre-compiler macro
// __IPHONE_2_1 will be defined for SDK 2.1 and later, but not on SDK 2.0
#ifdef __IPHONE_2_1
cell = (CustomCell *)[nib objectAtIndex:0];
#else
cell = (CustomCell *)[nib objectAtIndex:1];
#endif

}
NSUInteger row = [indexPath row];
NSDictionary *rowData = [self.computers objectAtIndex:row];
cell.colorLabel.text = [rowData objectForKey:@"Color"];
cell.nameLabel.text = [rowData objectForKey:@"Name"];
return cell;
}
Also, if you compile the code from the project archive against SDK 2.0, you might encounter a warning message like this:
iPhone Development
Projects/08 Cells/CellsViewController.xib:6: warning: UIScrollView's 'Bounce
Zoom' option will be ignored on iPhone OS versions prior to 2.1.
Don't worry about that - all it means is that the nib was opened in Interface Builder on a machine with SDK 2.1+ installed, and it added an attribute that is not used in SDK 2.0. That attribute will simply be ignored when compiled against SDK 2.0.

No comments:

Post a Comment