When you insert rows to a table view (or delete rows from a table view), you have to make sure that subsequent calls to the table view datasource methods provide a consistent worldview to the table view.
E.g. when the table view has 4 rows, and you insert 2 rows, a subsequent call to -numberOfRowsInTableView:
must return 6, and the -tableView:cellForRowAtIndexPath:
method should return the new data for the two newly inserted rows.
So far so good...
Now, there are race conditions in your code.
First race condition: you access the iVar originalItems
from different threads.
Second race condition: after you update the originalItems
array, but before the synchronized call on the main thread to -insertRowsAtIndexPaths:withRowAnimation:
, the table view may call datasource methods. This is causing the crash. When you do all the updating stuff on the main thread, the crash will go away...
Possible solution
I would do the updating on the main thread only! Therefore, you could create a method named -addItems:
that adds new rows to the tableview and which can be called from any thread. There will be no performance penalty if you do this. If the calculation of the newItems
array is expensive, you may still do it in another thread, but the actual update must be done on the main thread. Additionally, you can put a synchronized-block around every access to the originalItems
array, so that you can have read-only access from other threads.
// you can call this method from any thread
- (void)addItems:(NSArray *)newItems
{
// Ensure we are on the main thread
if(![NSThread isMainThread]) {
newItems = [newItems copy];
dispatch_async(dispatch_get_main_queue(), ^{
[self addItems:newItems];
});
return;
}
// We are on the main thread...
// Update the data source and insert the new items
NSMutableArray *indexPathArray;
@synchronized(self) {
NSUInteger currentItemsArraySize = originalItems.count;
[originalItems addObjectsFromArray:newItems];
indexPathArray = [NSMutableArray new];
for (NSUInteger i = currentItemsArraySize; i < (currentItemsArraySize + newItemsSize); i++) {
[indexPathArray addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
}
[self beginUpdates];
[self insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
[self endUpdates];
}