Okay, some people have been experiencing sporadic problems with the Chapter 4 application as described here. The solution I'd like to use would require being able to determine the number of pending, uncommitted section inserts and deletes that a table view has. Although I can get to this information, I can only do so by accessing a private instance variable of UITableView. Obviously, I don't want to give you all a solution that's going to get your application's rejected during the review process.
So, I went back to the drawing board. I don't like this solution as much since it requires us to duplicate work that the table view is already doing by keeping a shadow count of inserts and deletes, but it seems to work well and doesn't add too much complexity. I now have a pretty thorough test case for inserting and deleting rows from a table that uses an NSFetchedResultsController and this solution passes it, so fingers crossed.
The first step is to add a @private NSUInteger instance variables to the controller class that manages the table and fetched results controller. This will keep a running count of the number of sections inserted and deleted during a batch of table updates.
In context of the Chapter 4 application, that means adding the following bold line of code to HeroListViewController.h:
Now, we have to switch over to the implementation file, HeroListViewController.m and add a line of code to reset the insert count when we get notified by the fetched results controller that changes are coming. To do that, we add one line of code to the method controllerWillChangeContent:, like so:
Next, we have to increment this variable whenever we insert a section, and decrement it whenever we delete a section in controller:didChangeSection:atIndex:forChangeType:. We do that by adding the bold code below:
Finally, any time we do our consistency check in controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:, we have to take the pending inserts and deletes into account. Since we do the check more than once and insert new sections when the check fails, we also increment the variable if we do insert new rows.
So, I went back to the drawing board. I don't like this solution as much since it requires us to duplicate work that the table view is already doing by keeping a shadow count of inserts and deletes, but it seems to work well and doesn't add too much complexity. I now have a pretty thorough test case for inserting and deleting rows from a table that uses an NSFetchedResultsController and this solution passes it, so fingers crossed.
The Solution
The first step is to add a @private NSUInteger instance variables to the controller class that manages the table and fetched results controller. This will keep a running count of the number of sections inserted and deleted during a batch of table updates.
In context of the Chapter 4 application, that means adding the following bold line of code to HeroListViewController.h:
enum ;
@class HeroEditController;
Now, we have to switch over to the implementation file, HeroListViewController.m and add a line of code to reset the insert count when we get notified by the fetched results controller that changes are coming. To do that, we add one line of code to the method controllerWillChangeContent:, like so:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
Next, we have to increment this variable whenever we insert a section, and decrement it whenever we delete a section in controller:didChangeSection:atIndex:forChangeType:. We do that by adding the bold code below:
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:( )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
Finally, any time we do our consistency check in controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:, we have to take the pending inserts and deletes into account. Since we do the check more than once and insert new sections when the check fails, we also increment the variable if we do insert new rows.
We do all that by adding the bold code in below to that method:
I'll push this new code into the project archive as soon as possible and get it posted to apress.com and iphonedevbook.com, but here is the updated version of the Chapter 4 Xcode project in the meantime.
Don't worry if you don't understand everything that's going on in this code. This is nasty code designed to be completely generic so you don't have to worry about it at all. Hopefully this will be the end of our troubles with NSFetchedResultsController.
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
I'll push this new code into the project archive as soon as possible and get it posted to apress.com and iphonedevbook.com, but here is the updated version of the Chapter 4 Xcode project in the meantime.
Don't worry if you don't understand everything that's going on in this code. This is nasty code designed to be completely generic so you don't have to worry about it at all. Hopefully this will be the end of our troubles with NSFetchedResultsController.
0 comments:
Post a Comment