What's the best way to ensure a UITableView reloads atomically?

I'v got a UITableView whose dataSource updated at random intervals in a very short period of time. As more objects are discovered, they are added to the tableView's data source and I insert the specific indexPath:

[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];

The data source is located in a manager class, and a notification is posted when it changes.

- (void)addObjectToDataSource:(NSObject*)object {
    [self.dataSource addObject:object];
    [[NSNotificationCenter defaultCenter] postNotification:@"dataSourceUpdate" object:nil];
}

The viewController updates the tableView when it receives this notification.

- (void)handleDataSourceUpdate:(NSNotification*)notification {
    NSObject *object = notification.userInfo[@"object"];
    NSIndexPath *indexPath = [self indexPathForObject:object];

    [self.tableView beginUpdates];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
}

This works fine, but I noticed that in some cases, a second object is discovered just as the first one is calling endUpdates, and I get an exception claiming I have two objects in my data source when the tableView was expecting one.

I was wondering if anyone has figured out a better way to atomically insert rows into a tableView. I was thinking of putting a @synchronized(self.tableView) block around the update, but I'd like to avoid that if possible because it is expensive.

Answers


The method I've recommended is to create a private queue for synchronously posting batch updates onto the main queue (where addRow is a method that inserts an item into the data model at a given indexPath):

@interface MyModelClass ()
@property (strong, nonatomic) dispatch_queue_t myDispatchQueue;
@end

@implementation MyModelClass

- (dispatch_queue_t)myDispatchQueue
{
    if (_myDispatchQueue == nil) {
        _myDispatchQueue = dispatch_queue_create("myDispatchQueue", NULL);
    }
    return _myDispatchQueue;
}

- (void)addRow:(NSString *)data atIndexPath:(NSIndexPath *)indexPath
{
    dispatch_async(self.myDispatchQueue, ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            //update the data model here
            [self.tableView beginUpdates];
            [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [self.tableView endUpdates];
        });
    });
}

By doing it this way, you don't block any other threads and the block-based approach ensures that the table view's animation blocks (the ones that are throwing the exceptions) get executed in the right order. There is a more detailed explanation in Rapid row insertion into UITableView causes NSInternalInconsistencyException.


Need Your Help

Identifier not found error on function call

c++ compiler-errors

I have a program here where I invert the case of an entered string. This is the code in my .cpp file and I am using Visual Studio C++ IDE. I am not sure what I need in a header file or if I need on...

Can I use MySQL interactively from an SQL block in org-mode?

mysql sql emacs org-mode

I use R interactively from Org-mode - the keybinding C-x ' from within an R code-block launches an interactive R session using ESS.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.