Add some index path validation to avoid production crashes (#2716)

This commit is contained in:
Adlai Holler 2016-12-05 16:37:44 -08:00 committed by Hannah Troisi
parent 42c7bb291e
commit 45c69ada36
2 changed files with 71 additions and 1 deletions

View File

@ -358,6 +358,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
[self reloadDataWithCompletion:nil]; [self reloadDataWithCompletion:nil];
} }
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
{
if ([self validateIndexPath:indexPath]) {
[super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
}
}
- (void)reloadDataImmediately - (void)reloadDataImmediately
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
@ -620,8 +627,35 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
} }
} }
/**
* Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise.
*/
- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath
{
if (indexPath == nil) {
return nil;
}
NSInteger section = indexPath.section;
if (section >= self.numberOfSections) {
ASDisplayNodeFailAssert(@"Collection view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections);
return nil;
}
if (indexPath.item >= [self numberOfItemsInSection:section]) {
ASDisplayNodeFailAssert(@"Collection view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfItemsInSection:section]);
return nil;
}
return indexPath;
}
- (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath - (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath
{ {
if ([self validateIndexPath:indexPath] == nil) {
return nil;
}
// If this is a section index path, we don't currently have a method // If this is a section index path, we don't currently have a method
// to do a mapping. // to do a mapping.
if (indexPath.item == NSNotFound) { if (indexPath.item == NSNotFound) {
@ -656,7 +690,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
{ {
return [_dataController completedIndexPathForNode:cellNode]; return [self validateIndexPath:[_dataController completedIndexPathForNode:cellNode]];
} }
- (NSArray *)visibleNodes - (NSArray *)visibleNodes

View File

@ -467,6 +467,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[super reloadData]; [super reloadData];
} }
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
{
if ([self validateIndexPath:indexPath]) {
[super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
}
}
- (void)relayoutItems - (void)relayoutItems
{ {
[_dataController relayoutAllNodes]; [_dataController relayoutAllNodes];
@ -521,6 +528,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath - (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath
{ {
if ([self validateIndexPath:indexPath] == nil) {
return nil;
}
// If this is a section index path, we don't currently have a method // If this is a section index path, we don't currently have a method
// to do a mapping. // to do a mapping.
if (indexPath.row == NSNotFound) { if (indexPath.row == NSNotFound) {
@ -553,12 +564,37 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
return [self indexPathForNode:cellNode waitingIfNeeded:NO]; return [self indexPathForNode:cellNode waitingIfNeeded:NO];
} }
/**
* Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise.
*/
- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath
{
if (indexPath == nil) {
return nil;
}
NSInteger section = indexPath.section;
if (section >= self.numberOfSections) {
ASDisplayNodeFailAssert(@"Table view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections);
return nil;
}
if (indexPath.item >= [self numberOfRowsInSection:section]) {
ASDisplayNodeFailAssert(@"Table view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfRowsInSection:section]);
return nil;
}
return indexPath;
}
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait - (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait
{ {
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode]; NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
indexPath = [self validateIndexPath:indexPath];
if (indexPath == nil && wait) { if (indexPath == nil && wait) {
[_dataController waitUntilAllUpdatesAreCommitted]; [_dataController waitUntilAllUpdatesAreCommitted];
indexPath = [_dataController completedIndexPathForNode:cellNode]; indexPath = [_dataController completedIndexPathForNode:cellNode];
indexPath = [self validateIndexPath:indexPath];
} }
return indexPath; return indexPath;
} }