@@ -22,7 +22,7 @@ @interface ASRangeControllerBeta ()
2222 BOOL _rangeIsValid;
2323 BOOL _queuedRangeUpdate;
2424 ASScrollDirection _scrollDirection;
25- NSSet *_allPreviousIndexPaths;
25+ NSSet < NSIndexPath *> *_allPreviousIndexPaths;
2626}
2727
2828@end
@@ -65,7 +65,7 @@ - (void)_updateVisibleNodeIndexPaths
6565 }
6666
6767 // FIXME: Consider if we need to check this separately from the range calculation below.
68- NSArray *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController: self ];
68+ NSArray < NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController: self ];
6969
7070 if (visibleNodePaths.count == 0 ) { // if we don't have any visibleNodes currently (scrolled before or after content)...
7171 _queuedRangeUpdate = NO ;
@@ -80,19 +80,22 @@ - (void)_updateVisibleNodeIndexPaths
8080 [_layoutController setVisibleNodeIndexPaths: visibleNodePaths];
8181 }
8282
83- NSArray * allNodes = [_dataSource completedNodes ];
84- NSArray *currentSectionNodes = nil ;
83+ NSArray < NSArray *> * allNodes = [_dataSource completedNodes ]; // 2D array: section arrays, each containing nodes.
84+ NSArray <ASDisplayNode *> *currentSectionNodes = nil ;
8585 NSInteger currentSectionIndex = -1 ; // Will be unequal to any indexPath.section, so we set currentSectionNodes.
8686
8787 NSUInteger numberOfSections = [allNodes count ];
8888 NSUInteger numberOfNodesInSection = 0 ;
8989
90- NSSet *visibleIndexPaths = [NSSet setWithArray: visibleNodePaths];
90+ NSSet < NSIndexPath *> *visibleIndexPaths = [NSSet setWithArray: visibleNodePaths];
9191 // = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
92- NSSet *displayIndexPaths = nil ;
93- NSSet *fetchDataIndexPaths = nil ;
94- NSMutableSet *allIndexPaths = nil ;
95- NSMutableArray *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array ] : nil );
92+ NSSet <NSIndexPath *> *displayIndexPaths = nil ;
93+ NSSet <NSIndexPath *> *fetchDataIndexPaths = nil ;
94+ NSMutableArray <NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array ] : nil );
95+
96+ // Prioritize the order in which we visit each. Visible nodes should be updated first so they are enqueued on
97+ // the network or display queues before offscreen, preloading nodes are.
98+ NSMutableOrderedSet <NSIndexPath *> *allIndexPaths = [[NSMutableOrderedSet alloc ] initWithSet: visibleIndexPaths];
9699
97100 ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController: self ];
98101
@@ -102,20 +105,22 @@ - (void)_updateVisibleNodeIndexPaths
102105 displayIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeDisplay];
103106
104107 // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
105- allIndexPaths = [fetchDataIndexPaths mutableCopy ];
108+ // Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items.
109+ // This means that during iteration, we will first visit visible, then display, then fetch data nodes.
110+ // Nodes within the visible range may be getting their first display and fetch data call too, so enqueue them first.
106111 [allIndexPaths unionSet: displayIndexPaths];
107- [allIndexPaths unionSet: visibleIndexPaths];
108- } else {
109- allIndexPaths = [visibleIndexPaths mutableCopy ];
112+ [allIndexPaths unionSet: fetchDataIndexPaths];
110113 }
111114
112115 // Sets are magical. Add anything we had applied interfaceState to in the last update, so we can clear any
113116 // range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic
114117 // scroll or major main thread stall could cause entirely disjoint sets, but we must visit all.
115- NSSet *allCurrentIndexPaths = [allIndexPaths copy ];
118+
119+ // Calling set on NSMutableOrderedSet just references the underlying data store, so we must copy it.
120+ NSSet <NSIndexPath *> *allCurrentIndexPaths = [[allIndexPaths set ] copy ];
116121 [allIndexPaths unionSet: _allPreviousIndexPaths];
117122 _allPreviousIndexPaths = allCurrentIndexPaths;
118-
123+
119124 for (NSIndexPath *indexPath in allIndexPaths) {
120125 // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
121126 // For consistency, make sure each node knows that it should measure itself if something changes.
0 commit comments