diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 3b80baecd0..693f1653cc 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -24,7 +24,8 @@ const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; static void *kASSizingQueueContext = &kASSizingQueueContext; @interface ASDataController () { - NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this. + NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available. + NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable. NSMutableArray *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes. NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking. @@ -347,6 +348,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ + // Deep copy _completedNodes to _externalCompletedNodes. + // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. + _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes); + LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; }); @@ -363,6 +368,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ + // Now that the transaction is done, _completedNodes can be accessed externally again. + _externalCompletedNodes = nil; + LOG(@"endUpdatesWithCompletion - calling delegate end"); [_delegate dataController:self endUpdatesAnimated:animated completion:completion]; }); @@ -617,28 +625,31 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSUInteger)numberOfSections { ASDisplayNodeAssertMainThread(); - return [_completedNodes count]; + return [[self completedNodes] count]; } - (NSUInteger)numberOfRowsInSection:(NSUInteger)section { ASDisplayNodeAssertMainThread(); - return [_completedNodes[section] count]; + return [[self completedNodes][section] count]; } - (ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - return _completedNodes[indexPath.section][indexPath.row]; + return [self completedNodes][indexPath.section][indexPath.row]; } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode; { ASDisplayNodeAssertMainThread(); + NSArray *nodes = [self completedNodes]; + NSUInteger numberOfNodes = nodes.count; + // Loop through each section to look for the cellNode - for (NSUInteger i = 0; i < [_completedNodes count]; i++) { - NSArray *sectionNodes = _completedNodes[i]; + for (NSUInteger i = 0; i < numberOfNodes; i++) { + NSArray *sectionNodes = nodes[i]; NSUInteger cellIndex = [sectionNodes indexOfObjectIdenticalTo:cellNode]; if (cellIndex != NSNotFound) { return [NSIndexPath indexPathForRow:cellIndex inSection:i]; @@ -651,13 +662,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths { ASDisplayNodeAssertMainThread(); - return ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]); + return ASFindElementsInMultidimensionalArrayAtIndexPaths((NSMutableArray *)[self completedNodes], [indexPaths sortedArrayUsingSelector:@selector(compare:)]); } +/// Returns nodes that can be queried externally. _externalCompletedNodes is used if available, _completedNodes otherwise. - (NSArray *)completedNodes { ASDisplayNodeAssertMainThread(); - return _completedNodes; + return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes; } #pragma mark - Dealloc