Merge pull request #688 from nguyenhuy/DataConsistencyDuringUpdateTransactions

Ensure data consistency between ASDataController and its delegate while executing update transactions
This commit is contained in:
appleguy
2015-09-27 22:38:03 -07:00

View File

@@ -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