From 3a20c6a1f4af52ba9e044359394ddd566eba4bdb Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 12 Apr 2016 17:21:36 -0700 Subject: [PATCH 1/3] Add caching for delegate and datasource calls in ASCollectionView and ASTableView --- AsyncDisplayKit/ASCollectionView.mm | 86 +++++++++++++++++++++-------- AsyncDisplayKit/ASTableView.mm | 79 +++++++++++++++++++------- 2 files changed, 123 insertions(+), 42 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 0835dcfbe1..024185cff5 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -106,9 +106,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; NSMutableArray *_batchUpdateBlocks; BOOL _asyncDataFetchingEnabled; - BOOL _asyncDelegateImplementsScrollviewDidScroll; - BOOL _asyncDataSourceImplementsConstrainedSizeForNode; - BOOL _asyncDataSourceImplementsNodeBlockForItemAtIndexPath; _ASCollectionViewNodeSizeInvalidationContext *_queuedNodeSizeInvalidationContext; // Main thread only BOOL _isDeallocating; @@ -133,6 +130,25 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; * The collection view never queried your data source before the update to see that it actually had 0 items. */ BOOL _superIsPendingDataLoad; + + struct { + unsigned int asyncDelegateScrollViewDidScroll:1; + unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1; + unsigned int asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath:1; + unsigned int asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath:1; + unsigned int asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated:1; + unsigned int asyncDelegateCollectionViewWillBeginBatchFetchWithContext:1; + unsigned int asyncDelegateShouldBatchFetchForCollectionView:1; + } _asyncDelegateFlags; + + struct { + unsigned int asyncDataSourceConstrainedSizeForNode:1; + unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1; + unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1; + unsigned int asyncDataSourceCollectionViewLockDataSource:1; + unsigned int asyncDataSourceCollectionViewUnlockDataSource:1; + unsigned int asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath:1; + } _asyncDataSourceFlags; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -333,16 +349,26 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; if (asyncDataSource == nil) { _asyncDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDataSourceImplementsConstrainedSizeForNode = NO; - _asyncDataSourceImplementsNodeBlockForItemAtIndexPath = NO; + + _asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode = NO; + _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = NO; + _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = NO; + _asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource = NO; + _asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource = NO; + _asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = NO; } else { _asyncDataSource = asyncDataSource; _proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; - _asyncDataSourceImplementsConstrainedSizeForNode = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; - _asyncDataSourceImplementsNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)]; + + _asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]; + _asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]; + _asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource = [_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]; + _asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];; // Data-source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath: - ASDisplayNodeAssertTrue(_asyncDataSourceImplementsNodeBlockForItemAtIndexPath || [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)]); + ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode || _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath); } super.dataSource = (id)_proxyDataSource; @@ -363,11 +389,25 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; if (asyncDelegate == nil) { _asyncDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDelegateImplementsScrollviewDidScroll = NO; + + _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = NO; + _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = NO; + _asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath = NO; + _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = NO; + _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated = NO; + _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext = NO; + _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView = NO; } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; - _asyncDelegateImplementsScrollviewDidScroll = ([_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)] ? 1 : 0); + + _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; + _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]; } super.delegate = (id)_proxyDelegate; @@ -566,7 +606,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASCellNode *cellNode = [cell node]; cellNode.scrollView = collectionView; - if ([_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath) { [_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath]; } @@ -586,7 +626,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASCellNode *cellNode = [cell node]; - if ([_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNode:forItemAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath) { ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil."); [_asyncDelegate collectionView:self didEndDisplayingNode:cellNode forItemAtIndexPath:indexPath]; } @@ -597,7 +637,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated) { [_asyncDelegate collectionView:self didEndDisplayingNodeForItemAtIndexPath:indexPath]; } #pragma clang diagnostic pop @@ -620,7 +660,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; inScrollView:scrollView withCellFrame:collectionCell.frame]; } - if (_asyncDelegateImplementsScrollviewDidScroll) { + if (_asyncDelegateFlags.asyncDelegateScrollViewDidScroll) { [_asyncDelegate scrollViewDidScroll:scrollView]; } } @@ -637,7 +677,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset]; } - if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { + if (_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset) { [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; } } @@ -747,8 +787,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (BOOL)canBatchFetch { // if the delegate does not respond to this method, there is no point in starting to fetch - BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]; - if (canFetch && [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]) { + BOOL canFetch = _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext; + if (canFetch && _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView) { return [_asyncDelegate shouldBatchFetchForCollectionView:self]; } else { return canFetch; @@ -788,7 +828,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)_beginBatchFetching { [_batchContext beginBatchFetching]; - if ([_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]) { + if (_asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [_asyncDelegate collectionView:self willBeginBatchFetchWithContext:_batchContext]; }); @@ -800,7 +840,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath { - if (!_asyncDataSourceImplementsNodeBlockForItemAtIndexPath) { + if (!_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath) { ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath]; ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode"); __weak __typeof__(self) weakSelf = self; @@ -842,7 +882,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; // TODO: Move this logic into the flow layout inspector. Create a simple inspector for non-flow layouts that don't // implement a custom inspector. - if (_asyncDataSourceImplementsConstrainedSizeForNode) { + if (_asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode) { constrainedSize = [_asyncDataSource collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; } else { CGSize maxSize = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero) ? self.bounds.size : _maxSizeForNodesConstrainedSize; @@ -863,7 +903,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { - if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { + if (_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView) { return [_asyncDataSource numberOfSectionsInCollectionView:self]; } else { return 1; @@ -875,7 +915,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked"); self.asyncDataSourceLocked = YES; - if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) { + if (_asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource) { [_asyncDataSource collectionViewLockDataSource:self]; } } @@ -885,7 +925,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked"); self.asyncDataSourceLocked = NO; - if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) { + if (_asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource) { [_asyncDataSource collectionViewUnlockDataSource:self]; } } diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 6718932297..17068f47cd 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -112,9 +112,25 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL _ignoreNodesConstrainedWidthChange; BOOL _queuedNodeHeightUpdate; BOOL _isDeallocating; - BOOL _dataSourceImplementsNodeBlockForRowAtIndexPath; - BOOL _asyncDelegateImplementsScrollviewDidScroll; NSMutableSet *_cellsForVisibilityUpdates; + + struct { + unsigned int asyncDelegateScrollViewDidScroll:1; + unsigned int asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath:1; + unsigned int asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath:1; + unsigned int asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPathDeprecated:1; + unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1; + unsigned int asyncDelegateTableViewWillBeginBatchFetchWithContext:1; + unsigned int asyncDelegateShouldBatchFetchForTableView:1; + } _asyncDelegateFlags; + + struct { + unsigned int asyncDataSourceNumberOfSectionsInTableView:1; + unsigned int asyncDataSourceTableViewNodeBlockForRowAtIndexPath:1; + unsigned int asyncDataSourceTableViewNodeForRowAtIndexPath:1; + unsigned int asyncDataSourceTableViewLockDataSource:1; + unsigned int asyncDataSourceTableViewUnlockDataSource:1; + } _asyncDataSourceFlags; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -260,13 +276,24 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (asyncDataSource == nil) { _asyncDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; - _dataSourceImplementsNodeBlockForRowAtIndexPath = NO; + + _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = NO; + _asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = NO; + _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = NO; + _asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource = NO; + _asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource = NO; } else { _asyncDataSource = asyncDataSource; - _dataSourceImplementsNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]; - // Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath: - ASDisplayNodeAssertTrue(_dataSourceImplementsNodeBlockForRowAtIndexPath || [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)]); _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; + + _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]; + _asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewLockDataSource:)]; + _asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSource:)]; + + // Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath: + ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath || _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath); } super.dataSource = (id)_proxyDataSource; @@ -287,11 +314,25 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (asyncDelegate == nil) { _asyncDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDelegateImplementsScrollviewDidScroll = NO; + + _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = NO; + _asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath = NO; + _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath = NO; + _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPathDeprecated = NO; + _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = NO; + _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext = NO; + _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = NO; } else { _asyncDelegate = asyncDelegate; - _asyncDelegateImplementsScrollviewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; + + _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; + _asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNodeForRowAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]; } super.delegate = (id)_proxyDelegate; @@ -584,7 +625,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; ASCellNode *cellNode = [cell node]; cellNode.scrollView = tableView; - if ([_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath) { [_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath]; } @@ -609,7 +650,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]]; - if ([_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) { ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil."); [_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath]; } @@ -620,7 +661,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNodeForRowAtIndexPath:)]) { + if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPathDeprecated) { [_asyncDelegate tableView:self didEndDisplayingNodeForRowAtIndexPath:indexPath]; } #pragma clang diagnostic pop @@ -642,7 +683,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; inScrollView:scrollView withCellFrame:tableCell.frame]; } - if (_asyncDelegateImplementsScrollviewDidScroll) { + if (_asyncDelegateFlags.asyncDelegateScrollViewDidScroll) { [_asyncDelegate scrollViewDidScroll:scrollView]; } } @@ -659,7 +700,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset]; } - if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { + if (_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset) { [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; } } @@ -722,8 +763,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (BOOL)canBatchFetch { // if the delegate does not respond to this method, there is no point in starting to fetch - BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; - if (canFetch && [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]) { + BOOL canFetch = _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext; + if (canFetch && _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView) { return [_asyncDelegate shouldBatchFetchForTableView:self]; } else { return canFetch; @@ -763,7 +804,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)_beginBatchFetching { [_batchContext beginBatchFetching]; - if ([_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]) { + if (_asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext]; }); @@ -1013,7 +1054,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; self.asyncDataSourceLocked = YES; - if ([_asyncDataSource respondsToSelector:@selector(tableViewLockDataSource:)]) { + if (_asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource) { [_asyncDataSource tableViewLockDataSource:self]; } } @@ -1024,7 +1065,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; self.asyncDataSourceLocked = NO; - if ([_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSource:)]) { + if (_asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource) { [_asyncDataSource tableViewUnlockDataSource:self]; } } @@ -1036,7 +1077,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { - if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { + if (_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView) { return [_asyncDataSource numberOfSectionsInTableView:self]; } else { return 1; // default section number From d9f16e0acfcc34d7b8a9a0a574591ec7023cc437 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 12 Apr 2016 17:22:26 -0700 Subject: [PATCH 2/3] Use _performingBatchUpdates for _layoutFacilitator batched parameter --- AsyncDisplayKit/ASCollectionView.mm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 024185cff5..2055cce528 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -1042,8 +1042,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:_performingBatchUpdates]; if (_performingBatchUpdates) { - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:YES]; [_batchUpdateBlocks addObject:^{ [super insertItemsAtIndexPaths:indexPaths]; }]; @@ -1063,13 +1063,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:_performingBatchUpdates]; if (_performingBatchUpdates) { - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:YES]; [_batchUpdateBlocks addObject:^{ [super deleteItemsAtIndexPaths:indexPaths]; }]; } else { - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO]; [UIView performWithoutAnimation:^{ [super deleteItemsAtIndexPaths:indexPaths]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; @@ -1084,13 +1083,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:_performingBatchUpdates]; if (_performingBatchUpdates) { - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:YES]; [_batchUpdateBlocks addObject:^{ [super insertSections:indexSet]; }]; } else { - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO]; [UIView performWithoutAnimation:^{ [super insertSections:indexSet]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; @@ -1105,13 +1103,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:_performingBatchUpdates]; if (_performingBatchUpdates) { - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:YES]; [_batchUpdateBlocks addObject:^{ [super deleteSections:indexSet]; }]; } else { - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO]; [UIView performWithoutAnimation:^{ [super deleteSections:indexSet]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; From 8f1beb8bf6d78f6fe5ee4c2ed7ab73cfc570d755 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 18 Apr 2016 15:23:44 -0700 Subject: [PATCH 3/3] Addressed comments - Use memset to clear flags - Fix check for asyncDataSourceNodeForItemAtIndexPath - Fix selector for asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath - Fix multiple collectionViewWillEditCellsAtIndexPaths:batched: calls --- AsyncDisplayKit/ASCollectionView.mm | 22 ++++++---------------- AsyncDisplayKit/ASTableView.mm | 16 +++------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2055cce528..950cba8f76 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -143,6 +143,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; struct { unsigned int asyncDataSourceConstrainedSizeForNode:1; + unsigned int asyncDataSourceNodeForItemAtIndexPath:1; unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1; unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1; unsigned int asyncDataSourceCollectionViewLockDataSource:1; @@ -350,17 +351,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _asyncDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode = NO; - _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = NO; - _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = NO; - _asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource = NO; - _asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource = NO; - _asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = NO; + memset(&_asyncDataSourceFlags, 0, sizeof(_asyncDataSourceFlags)); } else { _asyncDataSource = asyncDataSource; _proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; _asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)]; _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)]; _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]; _asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]; @@ -368,7 +365,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];; // Data-source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath: - ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceConstrainedSizeForNode || _asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath); + ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath || _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath); } super.dataSource = (id)_proxyDataSource; @@ -390,13 +387,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _asyncDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = NO; - _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = NO; - _asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath = NO; - _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = NO; - _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated = NO; - _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext = NO; - _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView = NO; + memset(&_asyncDelegateFlags, 0, sizeof(_asyncDelegateFlags)); } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; @@ -405,7 +396,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; _asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)]; _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]; - _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]; + _asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNode:forItemAtIndexPath:)]; _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]; _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]; } @@ -1048,7 +1039,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [super insertItemsAtIndexPaths:indexPaths]; }]; } else { - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO]; [UIView performWithoutAnimation:^{ [super insertItemsAtIndexPaths:indexPaths]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 17068f47cd..5fe3d06694 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -277,18 +277,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _asyncDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = NO; - _asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = NO; - _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = NO; - _asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource = NO; - _asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource = NO; + memset(&_asyncDataSourceFlags, 0, sizeof(_asyncDataSourceFlags)); } else { _asyncDataSource = asyncDataSource; _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; _asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]; - _asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]; _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)]; + _asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]; _asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewLockDataSource:)]; _asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSource:)]; @@ -315,13 +311,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _asyncDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; - _asyncDelegateFlags.asyncDelegateScrollViewDidScroll = NO; - _asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath = NO; - _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath = NO; - _asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPathDeprecated = NO; - _asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = NO; - _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext = NO; - _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = NO; + memset(&_asyncDelegateFlags, 0, sizeof(_asyncDelegateFlags)); } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];