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.



8 comments:

darkcrayon said...

Thanks, Jeff!

I just got to that spot and was wondering what the problem was... And then when the sample download also failed to compile i figured something was up.

Also, in my PDF, the delegate method that's added at the bottom of page 208 is missing the constant value/constant after the "return"...

jonathan Saggau said...

It would be easier and much less error prone to enumerate the array looking for the object with the needed class.

Jeff LaMarche said...

Jonathan:

Thanks! Whiel I disagree that it necessarily would be easier - the precompiler macro is only necessary because we have to support multiple SDKs, you ordinarily wouldn't do that, so enumerating is definitely not easier than just grabbing a specific item, but you are definitely right that it would be less error prone, and we will probably re-write that way before the next printing.

Jeff

M M W said...

about macros test you could also test the object type

[aObject isKindOfClass:[UITableViewCell class]];

also a really misunderstood behaviour is the use of CustomCellIdentifier, this identifier should be unique for instance, build with the indexPath info + an autoincrement var

RedBrick Health said...

Nice find, however I'm having trouble figure it out a little past different versions of iPhone OS. Basically, the simulator expects objectAtIndex:1 whereas when I put the app on my phone, it expects objectAtIndex:0.

Ouch

Adam said...

this had me for about an hour before your post came back to me in a google search. thanks a mil!

JeansPilot said...

JeansPilot offers the chance to buy a large variety of men’s and women’s jeans clothing from the world famous Italian Brands.
Online jeans clothing store looks for original fashion clothing sales and clearances of worldwide known designers. We participate in fashion auctions to get the lowest possible price for Top quality Clothes, Shoes and Accessories.
Buy Jeans

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels