Quick Table View Weirdness
While working on a chapter of the iPhone book I came across an interesting feature/bug in table view.
Typically while adding content to a table view you don't want to call reloadData unless you absolutely have to because that pitches the whole stack of work that the table view did to get its drawing done. Instead you call one of insertRowsAtIndexPaths:withRowAnimation: or deleteRowsAtIndexPaths:withRowAnimation:. All this works fine if you start with at least one section. The example I was writing started with zero sections.
Adding a new object then calling insertRowsAtIndexPaths:withRowAnimation: leads to a really ugly error message. Like this;
2009-05-14 14:11:41.131 Conference[2716:20b] Serious application error. Exception was caught during ... change processing: *** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0) with userInfo (null) [Session started at 2009-05-14 14:11:41 -0600.] 2009-05-14 14:11:41.132 Conference[2716:20b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
If on the other hand you call insertSections:withRowAnimations: for that initial insert, everything is a-ok.
Hope this helps someone running into the same issue.









Bill, do you feel that insertRowsAtIndexPaths:withRowAnimation: can be called safely at arbitrary times? To handle a ever-growing table of images, I'm currently using reloadData for every new item that is added. The addition comes on the main thread from a callback of CFReadStream at the end of an asynchronous download. I'm getting a sort of hang (as if the event loop stops processing timers) associated with drag-scrolling the view, and I suspect that calling -reloadData during animation could be the problem. Now I'm considering going to insertRowsAtIndexPaths:withRowAnimation: instead of -reloadData all the time, which seems more lightweight.
Posted by Paul Collins on May 15, 2009 at 09:41 PM MDT #
Hi Paul,
You definitely want to be calling inserRows instead of reloading the table each time. This problem only comes up if you start with zero sections. When you add the first row in the first section you have to call addSection instead of insert rows.
Posted by Bill Dudney on May 15, 2009 at 09:41 PM MDT #
Hi Bill,
You comment both here and on the pragmatic forums about not wanting to call reloadData because of it being expensive. Does it really make such a big difference, calling reloadData compared to calling reloadRowsAtIndexPath? According to the documentation, reloadData only requests the rows that are actually visible, although indeed the view does need to call heightForRowAtIndexPath for each row again. If however that method just returns a static number (no custom row heights for each cell), the performance hit is not that big right?
Cheers - Daan
Posted by Daan van Dijk on May 16, 2009 at 01:26 PM MDT #
As always, the only way determine performance stuff for your application is to run instruments.
However the nice animation only comes with the insert rows solution.
Good luck!
Posted by bill Dudney on May 16, 2009 at 01:28 PM MDT #
Thanks Bill. I was getting this errors and you have saved me some troubleshooting time.
Have you filed a Radar bug with Apple? This should really be escalated and fixed on their end.
Posted by Adam on June 01, 2009 at 05:15 AM MDT #
Hope I can help someone with this error just in case, for me it was really frustrating. I had the [NSCFArray objectAtIndex:]: index (0) beyond bounds (0) with userInfo (null) error while adding an item. I changed this function to the following in stead of just getting the section count:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger count = [[resultsController sections] count];
if (count == 0) {
count = 1;
}
return count;
}
seemed that worked, I followed the recipe sample of apple this time better
Posted by Rene on January 26, 2010 at 06:09 PM MST #
When you create the UITableViewController it gives you those "dummy" fields where you have add integers. Namely:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1; //USE 1 INSTEAD OF 0
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [myArray count];
}
If you define the number of section being 0 i will run OK as long you do not add any rows. Make sure you set it to 1 at least.
It was pretty frustrating for me. Now I am happy.
Posted by TeddyHorse on February 28, 2010 at 09:29 PM MST #