Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 2e4d716

Browse files
Huy NguyenHuy Nguyen
authored andcommitted
ASDataController now handles reloadData more efficiently and notify its delegate once instead of a series of deletes and inserts
1 parent 3aceabb commit 2e4d716

8 files changed

Lines changed: 179 additions & 36 deletions

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ - (void)reloadDataWithCompletion:(void (^)())completion
241241
_superIsPendingDataLoad = YES;
242242
[super reloadData];
243243
});
244-
[_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion];
244+
[_dataController reloadDataWithCompletion:completion];
245245
}
246246

247247
- (void)reloadData
@@ -253,7 +253,7 @@ - (void)reloadDataImmediately
253253
{
254254
ASDisplayNodeAssertMainThread();
255255
_superIsPendingDataLoad = YES;
256-
[_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone];
256+
[_dataController reloadDataImmediately];
257257
[super reloadData];
258258
}
259259

@@ -929,6 +929,25 @@ - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAt
929929
}
930930
}
931931

932+
- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController
933+
{
934+
ASDisplayNodeAssertMainThread();
935+
936+
if (!self.asyncDataSource || _superIsPendingDataLoad) {
937+
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
938+
}
939+
940+
if (_performingBatchUpdates) {
941+
[_batchUpdateBlocks addObject:^{
942+
[super reloadData];
943+
}];
944+
} else {
945+
[UIView performWithoutAnimation:^{
946+
[super reloadData];
947+
}];
948+
}
949+
}
950+
932951
#pragma mark - ASCellNodeDelegate
933952

934953
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged

AsyncDisplayKit/ASTableView.mm

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,7 @@ - (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
298298

299299
- (void)reloadDataWithCompletion:(void (^)())completion
300300
{
301-
ASPerformBlockOnMainThread(^{
302-
[super reloadData];
303-
});
304-
[_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion];
301+
[_dataController reloadDataWithCompletion:completion];
305302
}
306303

307304
- (void)reloadData
@@ -312,8 +309,7 @@ - (void)reloadData
312309
- (void)reloadDataImmediately
313310
{
314311
ASDisplayNodeAssertMainThread();
315-
[_dataController reloadDataImmediatelyWithAnimationOptions:UITableViewRowAnimationNone];
316-
[super reloadData];
312+
[_dataController reloadDataImmediately];
317313
}
318314

319315
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
@@ -845,6 +841,18 @@ - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAt
845841
});
846842
}
847843

844+
- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController
845+
{
846+
ASDisplayNodeAssertMainThread();
847+
LOG(@"UITableView reloadData");
848+
849+
if (!self.asyncDataSource) {
850+
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
851+
}
852+
853+
[super reloadData];
854+
}
855+
848856
#pragma mark - ASDataControllerDelegate
849857

850858
- (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath

AsyncDisplayKit/Details/ASCollectionDataController.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ - (void)prepareForReloadData
5555

5656
- (void)willReloadData
5757
{
58-
[_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) {
58+
[_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) {
5959
// Remove everything that existed before the reload, now that we're ready to insert replacements
6060
NSArray *indexPaths = [self indexPathsForEditingNodesOfKind:kind];
6161
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];

AsyncDisplayKit/Details/ASDataController.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
9494
*/
9595
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
9696

97+
/**
98+
Called for data reload.
99+
*/
100+
- (void)dataControllerDidReloadData:(ASDataController *)dataController;
101+
97102
@end
98103

99104
/**
@@ -170,9 +175,9 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
170175

171176
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
172177

173-
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^ _Nullable)())completion;
178+
- (void)reloadDataWithCompletion:(void (^ _Nullable)())completion;
174179

175-
- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
180+
- (void)reloadDataImmediately;
176181

177182
/** @name Data Querying */
178183

AsyncDisplayKit/Details/ASDataController.mm

Lines changed: 108 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ @interface ASDataController () {
4343
BOOL _delegateDidDeleteNodes;
4444
BOOL _delegateDidInsertSections;
4545
BOOL _delegateDidDeleteSections;
46+
BOOL _delegateDidReloadData;
4647
}
4748

4849
@property (atomic, assign) NSUInteger batchUpdateCounter;
@@ -92,6 +93,7 @@ - (void)setDelegate:(id<ASDataControllerDelegate>)delegate
9293
_delegateDidDeleteNodes = [_delegate respondsToSelector:@selector(dataController:didDeleteNodes:atIndexPaths:withAnimationOptions:)];
9394
_delegateDidInsertSections = [_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:withAnimationOptions:)];
9495
_delegateDidDeleteSections = [_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:withAnimationOptions:)];
96+
_delegateDidReloadData = [_delegate respondsToSelector:@selector(dataControllerDidReloadData:)];
9597
}
9698

9799
+ (NSUInteger)parallelProcessorCount
@@ -143,14 +145,30 @@ - (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrai
143145
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
144146
}
145147

148+
/**
149+
* Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store.
150+
*/
151+
- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths
152+
{
153+
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:NO withAnimationOptions:0];
154+
}
155+
146156
/**
147157
* Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store.
148158
*/
149159
- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
160+
{
161+
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:YES withAnimationOptions:animationOptions];
162+
}
163+
164+
/**
165+
* Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store.
166+
*/
167+
- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
150168
{
151169
[self batchLayoutNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
152170
// Insert finished nodes into data storage
153-
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
171+
[self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:shouldNotifyDelegate withAnimationOptions:animationOptions];
154172
}];
155173
}
156174

@@ -240,6 +258,14 @@ - (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths co
240258
}];
241259
}
242260

261+
- (void)deleteAllNodesOfKind:(NSString *)kind
262+
{
263+
[_editingNodes[kind] removeAllObjects];
264+
[_mainSerialQueue performBlockOnMainThread:^{
265+
[_completedNodes[kind] removeAllObjects];
266+
}];
267+
}
268+
243269
- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock
244270
{
245271
if (indexSet.count == 0)
@@ -277,6 +303,17 @@ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet
277303

278304
#pragma mark - Internal Data Querying + Editing
279305

306+
/**
307+
* Inserts the specified nodes into the given index paths and doesn't notify the delegate of newly inserted nodes.
308+
*
309+
* @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy
310+
* of the editing nodes.
311+
*/
312+
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths
313+
{
314+
[self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:NO withAnimationOptions:0];
315+
}
316+
280317
/**
281318
* Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes.
282319
*
@@ -285,10 +322,26 @@ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet
285322
*/
286323
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
287324
{
288-
[self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
289-
if (_delegateDidInsertNodes)
290-
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
291-
}];
325+
[self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:YES withAnimationOptions:animationOptions];
326+
}
327+
328+
/**
329+
* Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes.
330+
*
331+
* @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy
332+
* of the editing nodes. The delegate is invoked on the main thread.
333+
*/
334+
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
335+
{
336+
void (^completionBlock)(NSArray *nodes, NSArray *indexPaths) = nil;
337+
if (shouldNotifyDelegate) {
338+
completionBlock = ^(NSArray *nodes, NSArray *indexPaths) {
339+
if (_delegateDidInsertNodes)
340+
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
341+
};
342+
}
343+
344+
[self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:completionBlock];
292345
}
293346

294347
/**
@@ -305,18 +358,46 @@ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASD
305358
}];
306359
}
307360

361+
/**
362+
* Inserts sections, represented as arrays, into the backing store at the given indicies and doesn't notify the delegate.
363+
*
364+
* @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted
365+
* in the completed store on the main thread.
366+
*/
367+
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet
368+
{
369+
[self _insertSections:sections atIndexSet:indexSet andNotifyDelegate:NO withAnimationOptions:0];
370+
}
371+
372+
/**
373+
* Inserts sections, represented as arrays, into the backing store at the given indicies and doesn't notify the delegate.
374+
*
375+
* @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted
376+
* in the completed store on the main thread.
377+
*/
378+
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
379+
{
380+
[self _insertSections:sections atIndexSet:indexSet andNotifyDelegate:YES withAnimationOptions:animationOptions];
381+
}
382+
308383
/**
309384
* Inserts sections, represented as arrays, into the backing store at the given indicies and notifies the delegate.
310385
*
311386
* @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted
312387
* in the completed store on the main thread. The delegate is invoked on the main thread.
313388
*/
314-
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
389+
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
315390
{
316-
[self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) {
317-
if (_delegateDidInsertSections)
318-
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
319-
}];
391+
392+
void (^completionBlock)(NSArray *sections, NSIndexSet *indexSet) = nil;
393+
if (shouldNotifyDelegate) {
394+
completionBlock = ^(NSArray *sections, NSIndexSet *indexSet) {
395+
if (_delegateDidInsertSections)
396+
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
397+
};
398+
}
399+
400+
[self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:completionBlock];
320401
}
321402

322403
/**
@@ -361,17 +442,17 @@ - (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions
361442
}];
362443
}
363444

364-
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion
445+
- (void)reloadDataWithCompletion:(void (^)())completion
365446
{
366-
[self _reloadDataWithAnimationOptions:animationOptions synchronously:NO completion:completion];
447+
[self _reloadDataSynchronously:NO completion:completion];
367448
}
368449

369-
- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
450+
- (void)reloadDataImmediately
370451
{
371-
[self _reloadDataWithAnimationOptions:animationOptions synchronously:YES completion:nil];
452+
[self _reloadDataSynchronously:YES completion:nil];
372453
}
373454

374-
- (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions synchronously:(BOOL)synchronously completion:(void (^)())completion
455+
- (void)_reloadDataSynchronously:(BOOL)synchronously completion:(void (^)())completion
375456
{
376457
[self performEditCommandWithBlock:^{
377458
ASDisplayNodeAssertMainThread();
@@ -393,12 +474,7 @@ - (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animat
393474
LOG(@"Edit Transaction - reloadData");
394475

395476
// Remove everything that existed before the reload, now that we're ready to insert replacements
396-
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]);
397-
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
398-
399-
NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind];
400-
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)];
401-
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
477+
[self deleteAllNodesOfKind:ASDataControllerRowNodeKind];
402478

403479
[self willReloadData];
404480

@@ -408,12 +484,19 @@ - (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animat
408484
[sections addObject:[[NSMutableArray alloc] init]];
409485
}
410486

411-
[self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withAnimationOptions:animationOptions];
487+
[self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]];
412488

413-
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
489+
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths];
414490

415-
if (completion) {
416-
dispatch_async(dispatch_get_main_queue(), completion);
491+
if (_delegateDidReloadData || completion) {
492+
[_mainSerialQueue performBlockOnMainThread:^{
493+
if (_delegateDidReloadData) {
494+
[_delegate dataControllerDidReloadData:self];
495+
}
496+
if (completion) {
497+
completion();
498+
}
499+
}];
417500
}
418501
};
419502

AsyncDisplayKit/Details/ASRangeController.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ NS_ASSUME_NONNULL_BEGIN
190190
*/
191191
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
192192

193+
/**
194+
* Called for data reload.
195+
*
196+
* @param rangeController Sender.
197+
*/
198+
- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController;
199+
193200
@end
194201

195202
NS_ASSUME_NONNULL_END

AsyncDisplayKit/Details/ASRangeController.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,17 @@ - (void)dataController:(ASDataController *)dataController didDeleteSectionsAtInd
276276
});
277277
}
278278

279+
- (void)dataControllerDidReloadData:(ASDataController *)dataController
280+
{
281+
ASPerformBlockOnMainThread(^{
282+
_rangeIsValid = NO;
283+
284+
// When reload data we need to make sure that _rangeTypeIndexPaths is cleared as well,
285+
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
286+
[_rangeTypeIndexPaths removeAllObjects];
287+
288+
[_delegate rangeControllerDidReloadData:self];
289+
});
290+
}
291+
279292
@end

AsyncDisplayKit/Details/ASRangeControllerBeta.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,12 @@ - (void)dataController:(ASDataController *)dataController didDeleteSectionsAtInd
267267
});
268268
}
269269

270+
- (void)dataControllerDidReloadData:(ASDataController *)dataController
271+
{
272+
ASPerformBlockOnMainThread(^{
273+
_rangeIsValid = NO;
274+
[_delegate rangeControllerDidReloadData:self];
275+
});
276+
}
277+
270278
@end

0 commit comments

Comments
 (0)