Our application has a UICollectionView , and its dataSource dictionary is dataSource regularly. We will never know when the next update will happen. The reload Collection View method can be called after the user clicks the button or it can happen asynchronously after the success of the network request. Given the above information, we risk having a race condition while reloading the collection and updating the data source. We even registered the following failure, and we believe that this was due to the race condition described above. Failure Message:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
collectionViewLayout sizeForItemAtIndexPath: method: collectionViewLayout sizeForItemAtIndexPath:
This method calculates the height for the collection view section based on the number of elements in sectionWithProducts . It will work because the dataSource counter dataSource less than indexPath.row . And the line causing the failure:
NSArray *sectionWithProducts = self.dataSource[indexPath.row];
The following lines called before the failure occurred:
[self.collectionView setCollectionViewLayout:[self flowLayout] animated:NO]; [self.collectionView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO]; [self.collectionView reloadData];
To prevent this, we decided to put a single line of code that updates the data source in the main stream.
// Always run on main thread in hope to prevent NSRangeException. // Reloading data should happen only sequential. dispatch_async(dispatch_get_main_queue(), ^(void) { self.dataSource = newValue; });
There was a lot of [self.collectionView reloadData] in our code. Is it worth it to run on the main topic? This happened quickly, so it should not block the user interface for long.
Are there UICollectionViewDelegateFlowLayout delegate methods with the indexPath property always called in the background queue?