mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
[ASDataController] Cancel if we lose our data source, fix bugs (#1987)
[ASRangeController] We're already on main thread, remove blocks Make data source read-only, clarify what's asynchronous [ASDataController] Clean up some interfaces [ASDataController] A little more cleanup [ASDataController] Cleanup [ASDataController] Restore some changes, exit more often [ASDataController] Use item counts that we already have rather than requerying them [ASDataController] Revert weakifications [ASDataController] Add a mechanism to measure how much work we avoided
This commit is contained in:
@@ -87,7 +87,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASCollectionView.
|
#pragma mark ASCollectionView.
|
||||||
|
|
||||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeInteractionDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView, ASDataControllerEnvironmentDelegate> {
|
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASCollectionDataControllerSource, ASCellNodeInteractionDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView, ASDataControllerEnvironmentDelegate> {
|
||||||
ASCollectionViewProxy *_proxyDataSource;
|
ASCollectionViewProxy *_proxyDataSource;
|
||||||
ASCollectionViewProxy *_proxyDelegate;
|
ASCollectionViewProxy *_proxyDelegate;
|
||||||
|
|
||||||
@@ -232,9 +232,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
_rangeController.delegate = self;
|
_rangeController.delegate = self;
|
||||||
_rangeController.layoutController = _layoutController;
|
_rangeController.layoutController = _layoutController;
|
||||||
|
|
||||||
_dataController = [[ASCollectionDataController alloc] init];
|
_dataController = [[ASCollectionDataController alloc] initWithDataSource:self];
|
||||||
_dataController.delegate = _rangeController;
|
_dataController.delegate = _rangeController;
|
||||||
_dataController.dataSource = self;
|
|
||||||
_dataController.environmentDelegate = self;
|
_dataController.environmentDelegate = self;
|
||||||
|
|
||||||
_batchContext = [[ASBatchContext alloc] init];
|
_batchContext = [[ASBatchContext alloc] init];
|
||||||
|
|||||||
@@ -189,8 +189,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
_rangeController.dataSource = self;
|
_rangeController.dataSource = self;
|
||||||
_rangeController.delegate = self;
|
_rangeController.delegate = self;
|
||||||
|
|
||||||
_dataController = [[dataControllerClass alloc] init];
|
_dataController = [[dataControllerClass alloc] initWithDataSource:self];
|
||||||
_dataController.dataSource = self;
|
|
||||||
_dataController.delegate = _rangeController;
|
_dataController.delegate = _rangeController;
|
||||||
_dataController.environmentDelegate = self;
|
_dataController.environmentDelegate = self;
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
|
|
||||||
@interface ASCollectionDataController : ASChangeSetDataController
|
@interface ASCollectionDataController : ASChangeSetDataController
|
||||||
|
|
||||||
|
- (instancetype)initWithDataSource:(id<ASCollectionDataControllerSource>)dataSource NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -31,11 +31,14 @@
|
|||||||
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
|
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)initWithDataSource:(id<ASCollectionDataControllerSource>)dataSource
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super initWithDataSource:dataSource];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_pendingContexts = [NSMutableDictionary dictionary];
|
_pendingContexts = [NSMutableDictionary dictionary];
|
||||||
|
_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath = [dataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:)];
|
||||||
|
|
||||||
|
ASDisplayNodeAssertTrue(_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath || [dataSource respondsToSelector:@selector(dataController:supplementaryNodeOfKind:atIndexPath:)]);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,7 @@
|
|||||||
}
|
}
|
||||||
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil];
|
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil];
|
||||||
|
|
||||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
@@ -95,7 +98,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil];
|
[self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil];
|
||||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
@@ -145,7 +148,7 @@
|
|||||||
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||||
{
|
{
|
||||||
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
|
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
|
||||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
@@ -178,7 +181,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self batchLayoutNodesFromContexts:reinsertedContexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:reinsertedContexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -240,20 +243,20 @@
|
|||||||
|
|
||||||
- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection
|
- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection
|
||||||
{
|
{
|
||||||
ASCellNodeBlock supplementaryCellBlock;
|
ASCellNodeBlock supplementaryCellBlock;
|
||||||
if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) {
|
if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) {
|
||||||
supplementaryCellBlock = [self.collectionDataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
|
supplementaryCellBlock = [self.collectionDataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
|
||||||
} else {
|
} else {
|
||||||
ASCellNode *supplementaryNode = [self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath];
|
ASCellNode *supplementaryNode = [self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath];
|
||||||
supplementaryCellBlock = ^{ return supplementaryNode; };
|
supplementaryCellBlock = ^{ return supplementaryNode; };
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||||
ASIndexedNodeContext *context = [[ASIndexedNodeContext alloc] initWithNodeBlock:supplementaryCellBlock
|
ASIndexedNodeContext *context = [[ASIndexedNodeContext alloc] initWithNodeBlock:supplementaryCellBlock
|
||||||
indexPath:indexPath
|
indexPath:indexPath
|
||||||
constrainedSize:constrainedSize
|
constrainedSize:constrainedSize
|
||||||
environmentTraitCollection:environmentTraitCollection];
|
environmentTraitCollection:environmentTraitCollection];
|
||||||
[contexts addObject:context];
|
[contexts addObject:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Sizing query
|
#pragma mark - Sizing query
|
||||||
@@ -296,12 +299,4 @@
|
|||||||
return (id<ASCollectionDataControllerSource>)self.dataSource;
|
return (id<ASCollectionDataControllerSource>)self.dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDataSource:(id<ASDataControllerSource>)dataSource
|
|
||||||
{
|
|
||||||
[super setDataSource:dataSource];
|
|
||||||
_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath = [self.collectionDataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:)];
|
|
||||||
|
|
||||||
ASDisplayNodeAssertTrue(_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath || [self.collectionDataSource respondsToSelector:@selector(dataController:supplementaryNodeOfKind:atIndexPath:)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -109,10 +109,12 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
|
|||||||
@protocol ASFlowLayoutControllerDataSource;
|
@protocol ASFlowLayoutControllerDataSource;
|
||||||
@interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource>
|
@interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource>
|
||||||
|
|
||||||
|
- (instancetype)initWithDataSource:(id<ASDataControllerSource>)dataSource NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Data source for fetching data info.
|
Data source for fetching data info.
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, weak) id<ASDataControllerSource> dataSource;
|
@property (nonatomic, weak, readonly) id<ASDataControllerSource> dataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Delegate to notify when data is updated.
|
Delegate to notify when data is updated.
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||||
#define LOG(...)
|
#define LOG(...)
|
||||||
|
|
||||||
|
#define AS_MEASURE_AVOIDED_DATACONTROLLER_WORK 0
|
||||||
|
|
||||||
|
#define RETURN_IF_NO_DATASOURCE(val) if (_dataSource == nil) { return val; }
|
||||||
#define ASSERT_ON_EDITING_QUEUE ASDisplayNodeAssertNotNil(dispatch_get_specific(&kASDataControllerEditingQueueKey), @"%@ must be called on the editing transaction queue.", NSStringFromSelector(_cmd))
|
#define ASSERT_ON_EDITING_QUEUE ASDisplayNodeAssertNotNil(dispatch_get_specific(&kASDataControllerEditingQueueKey), @"%@ must be called on the editing transaction queue.", NSStringFromSelector(_cmd))
|
||||||
|
|
||||||
const static NSUInteger kASDataControllerSizingCountPerProcessor = 5;
|
const static NSUInteger kASDataControllerSizingCountPerProcessor = 5;
|
||||||
@@ -31,6 +34,13 @@ const static char * kASDataControllerEditingQueueContext = "kASDataControllerEdi
|
|||||||
|
|
||||||
NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||||
|
|
||||||
|
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
|
||||||
|
@interface ASDataController (AvoidedWorkMeasuring)
|
||||||
|
+ (void)_didLayoutNode;
|
||||||
|
+ (void)_expectToInsertNodes:(NSUInteger)count;
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface ASDataController () {
|
@interface ASDataController () {
|
||||||
NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available.
|
NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available.
|
||||||
NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
|
NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
|
||||||
@@ -60,13 +70,15 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
|
|
||||||
#pragma mark - Lifecycle
|
#pragma mark - Lifecycle
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)initWithDataSource:(id<ASDataControllerSource>)dataSource
|
||||||
{
|
{
|
||||||
if (!(self = [super init])) {
|
if (!(self = [super init])) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
ASDisplayNodeAssert(![self isMemberOfClass:[ASDataController class]], @"ASDataController is an abstract class and should not be instantiated. Instantiate a subclass instead.");
|
ASDisplayNodeAssert(![self isMemberOfClass:[ASDataController class]], @"ASDataController is an abstract class and should not be instantiated. Instantiate a subclass instead.");
|
||||||
|
|
||||||
|
_dataSource = dataSource;
|
||||||
|
|
||||||
_completedNodes = [NSMutableDictionary dictionary];
|
_completedNodes = [NSMutableDictionary dictionary];
|
||||||
_editingNodes = [NSMutableDictionary dictionary];
|
_editingNodes = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
@@ -87,6 +99,13 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
ASDisplayNodeFailAssert(@"Failed to call designated initializer.");
|
||||||
|
id<ASDataControllerSource> fakeDataSource = nil;
|
||||||
|
return [self initWithDataSource:fakeDataSource];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setDelegate:(id<ASDataControllerDelegate>)delegate
|
- (void)setDelegate:(id<ASDataControllerDelegate>)delegate
|
||||||
{
|
{
|
||||||
if (_delegate == delegate) {
|
if (_delegate == delegate) {
|
||||||
@@ -116,10 +135,13 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
|
|
||||||
#pragma mark - Cell Layout
|
#pragma mark - Cell Layout
|
||||||
|
|
||||||
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler
|
||||||
{
|
{
|
||||||
ASSERT_ON_EDITING_QUEUE;
|
ASSERT_ON_EDITING_QUEUE;
|
||||||
|
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
|
||||||
|
[ASDataController _expectToInsertNodes:contexts.count];
|
||||||
|
#endif
|
||||||
|
|
||||||
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
|
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
|
||||||
NSUInteger count = contexts.count;
|
NSUInteger count = contexts.count;
|
||||||
|
|
||||||
@@ -127,7 +149,9 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
for (NSUInteger i = 0; i < count; i += blockSize) {
|
for (NSUInteger i = 0; i < count; i += blockSize) {
|
||||||
NSRange batchedRange = NSMakeRange(i, MIN(count - i, blockSize));
|
NSRange batchedRange = NSMakeRange(i, MIN(count - i, blockSize));
|
||||||
NSArray<ASIndexedNodeContext *> *batchedContexts = [contexts subarrayWithRange:batchedRange];
|
NSArray<ASIndexedNodeContext *> *batchedContexts = [contexts subarrayWithRange:batchedRange];
|
||||||
[self _layoutNodesFromContexts:batchedContexts ofKind:kind completion:completionBlock];
|
NSArray *nodes = [self _layoutNodesFromContexts:batchedContexts];
|
||||||
|
NSArray *indexPaths = [ASIndexedNodeContext indexPathsFromContexts:batchedContexts];
|
||||||
|
batchCompletionHandler(nodes, indexPaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,63 +168,58 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
/**
|
/**
|
||||||
* Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store.
|
* Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store.
|
||||||
*/
|
*/
|
||||||
- (void)_batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_batchLayoutAndInsertNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
ASSERT_ON_EDITING_QUEUE;
|
ASSERT_ON_EDITING_QUEUE;
|
||||||
|
|
||||||
[self batchLayoutNodesFromContexts:contexts ofKind:ASDataControllerRowNodeKind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
// Insert finished nodes into data storage
|
// Insert finished nodes into data storage
|
||||||
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
- (NSArray<ASCellNode *> *)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts
|
||||||
{
|
{
|
||||||
ASSERT_ON_EDITING_QUEUE;
|
ASSERT_ON_EDITING_QUEUE;
|
||||||
|
|
||||||
if (!contexts.count || _dataSource == nil) {
|
NSUInteger nodeCount = contexts.count;
|
||||||
return;
|
if (!nodeCount || _dataSource == nil) {
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSUInteger nodeCount = contexts.count;
|
|
||||||
__strong NSIndexPath **allocatedContextIndexPaths = (__strong NSIndexPath **)calloc(nodeCount, sizeof(NSIndexPath *));
|
|
||||||
__strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(nodeCount, sizeof(ASCellNode *));
|
__strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(nodeCount, sizeof(ASCellNode *));
|
||||||
|
|
||||||
for (NSUInteger j = 0; j < nodeCount; j += kASDataControllerSizingCountPerProcessor) {
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||||
NSInteger batchCount = MIN(kASDataControllerSizingCountPerProcessor, nodeCount - j);
|
dispatch_apply(nodeCount, queue, ^(size_t i) {
|
||||||
|
RETURN_IF_NO_DATASOURCE();
|
||||||
|
|
||||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
// Allocate the node.
|
||||||
dispatch_apply(batchCount, queue, ^(size_t i) {
|
ASIndexedNodeContext *context = contexts[i];
|
||||||
unsigned long k = j + i;
|
ASCellNode *node = [context allocateNode];
|
||||||
ASIndexedNodeContext *context = contexts[k];
|
if (node == nil) {
|
||||||
ASCellNode *node = [context allocateNode];
|
ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource);
|
||||||
if (node == nil) {
|
node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps.
|
||||||
ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource);
|
}
|
||||||
node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps.
|
|
||||||
}
|
[self _layoutNode:node withConstrainedSize:context.constrainedSize];
|
||||||
|
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
|
||||||
allocatedContextIndexPaths[k] = context.indexPath;
|
[ASDataController _didLayoutNode];
|
||||||
allocatedNodeBuffer[k] = node;
|
#endif
|
||||||
|
allocatedNodeBuffer[i] = node;
|
||||||
[self _layoutNode:node withConstrainedSize:context.constrainedSize];
|
});
|
||||||
});
|
|
||||||
}
|
BOOL canceled = _dataSource == nil;
|
||||||
|
|
||||||
// Create nodes and indexPaths array's
|
// Create nodes array
|
||||||
NSArray *allocatedNodes = [NSArray arrayWithObjects:allocatedNodeBuffer count:nodeCount];
|
NSArray *nodes = canceled ? nil : [NSArray arrayWithObjects:allocatedNodeBuffer count:nodeCount];
|
||||||
NSArray *indexPaths = [NSArray arrayWithObjects:allocatedContextIndexPaths count:nodeCount];
|
|
||||||
|
|
||||||
// Nil out buffer indexes to allow arc to free the stored cells.
|
// Nil out buffer indexes to allow arc to free the stored cells.
|
||||||
for (int i = 0; i < nodeCount; i++) {
|
for (int i = 0; i < nodeCount; i++) {
|
||||||
allocatedContextIndexPaths[i] = nil;
|
|
||||||
allocatedNodeBuffer[i] = nil;
|
allocatedNodeBuffer[i] = nil;
|
||||||
}
|
}
|
||||||
free(allocatedContextIndexPaths);
|
|
||||||
free(allocatedNodeBuffer);
|
free(allocatedNodeBuffer);
|
||||||
|
|
||||||
if (completionBlock) {
|
return nodes;
|
||||||
completionBlock(allocatedNodes, indexPaths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||||
@@ -238,9 +257,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForTwoDimensionalArray(_editingNodes[kind]));
|
LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForTwoDimensionalArray(_editingNodes[kind]));
|
||||||
NSMutableArray *editingNodes = _editingNodes[kind];
|
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[kind], indexPaths);
|
||||||
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
|
|
||||||
_editingNodes[kind] = editingNodes;
|
|
||||||
|
|
||||||
[_mainSerialQueue performBlockOnMainThread:^{
|
[_mainSerialQueue performBlockOnMainThread:^{
|
||||||
NSMutableArray *allNodes = _completedNodes[kind];
|
NSMutableArray *allNodes = _completedNodes[kind];
|
||||||
@@ -383,13 +400,10 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||||
|
|
||||||
NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self];
|
[self invalidateDataSourceItemCounts];
|
||||||
|
NSUInteger sectionCount = [self itemCountsFromDataSource].size();
|
||||||
NSIndexSet *sectionIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
NSIndexSet *sectionIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
||||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
|
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
|
||||||
|
|
||||||
[self invalidateDataSourceItemCounts];
|
|
||||||
// Fetch the new item counts upfront.
|
|
||||||
[self itemCountsFromDataSource];
|
|
||||||
|
|
||||||
// Allow subclasses to perform setup before going into the edit transaction
|
// Allow subclasses to perform setup before going into the edit transaction
|
||||||
[self prepareForReloadData];
|
[self prepareForReloadData];
|
||||||
@@ -416,10 +430,10 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
}
|
}
|
||||||
[self _insertSections:sections atIndexSet:sectionIndexSet withAnimationOptions:animationOptions];
|
[self _insertSections:sections atIndexSet:sectionIndexSet withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
if (completion) {
|
if (completion) {
|
||||||
dispatch_async(dispatch_get_main_queue(), completion);
|
[_mainSerialQueue performBlockOnMainThread:completion];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (synchronously) {
|
if (synchronously) {
|
||||||
@@ -455,10 +469,11 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||||
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
||||||
|
|
||||||
|
std::vector<NSInteger> counts = [self itemCountsFromDataSource];
|
||||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||||
for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) {
|
for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) {
|
||||||
NSUInteger itemCount = [_dataSource dataController:self rowsInSection:sectionIndex];
|
NSUInteger itemCount = counts[sectionIndex];
|
||||||
for (NSUInteger i = 0; i < itemCount; i++) {
|
for (NSUInteger i = 0; i < itemCount; i++) {
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIndex];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIndex];
|
||||||
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
||||||
@@ -585,6 +600,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
{
|
{
|
||||||
[self performEditCommandWithBlock:^{
|
[self performEditCommandWithBlock:^{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
LOG(@"Edit Command - insertSections: %@", sections);
|
LOG(@"Edit Command - insertSections: %@", sections);
|
||||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||||
|
|
||||||
@@ -603,7 +619,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
|
|
||||||
[self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions];
|
[self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -645,16 +661,16 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
// remove elements
|
// remove elements
|
||||||
|
|
||||||
LOG(@"Edit Transaction - moveSection");
|
LOG(@"Edit Transaction - moveSection");
|
||||||
|
NSMutableArray *editingRows = _editingNodes[ASDataControllerRowNodeKind];
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], [NSIndexSet indexSetWithIndex:section]);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(editingRows, [NSIndexSet indexSetWithIndex:section]);
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], indexPaths);
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(editingRows, indexPaths);
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
// update the section of indexpaths
|
// update the section of indexpaths
|
||||||
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection];
|
|
||||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
for (NSIndexPath *indexPath in indexPaths) {
|
for (NSIndexPath *indexPath in indexPaths) {
|
||||||
[updatedIndexPaths addObject:[sectionIndexPath indexPathByAddingIndex:[indexPath indexAtPosition:indexPath.length - 1]]];
|
NSIndexPath *updatedIndexPath = [NSIndexPath indexPathForItem:indexPath.item inSection:newSection];
|
||||||
|
[updatedIndexPaths addObject:updatedIndexPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't re-calculate size for moving
|
// Don't re-calculate size for moving
|
||||||
@@ -747,7 +763,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
[self willInsertRowsAtIndexPaths:indexPaths];
|
[self willInsertRowsAtIndexPaths:indexPaths];
|
||||||
|
|
||||||
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
|
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -812,11 +828,10 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
for (NSMutableArray *section in nodes) {
|
for (NSMutableArray *section in nodes) {
|
||||||
NSUInteger rowIndex = 0;
|
NSUInteger rowIndex = 0;
|
||||||
for (ASCellNode *node in section) {
|
for (ASCellNode *node in section) {
|
||||||
|
RETURN_IF_NO_DATASOURCE();
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||||
CGRect frame = CGRectZero;
|
[self _layoutNode:node withConstrainedSize:constrainedSize];
|
||||||
frame.size = [node measureWithSizeRange:constrainedSize].size;
|
|
||||||
node.frame = frame;
|
|
||||||
rowIndex += 1;
|
rowIndex += 1;
|
||||||
}
|
}
|
||||||
sectionIndex += 1;
|
sectionIndex += 1;
|
||||||
@@ -940,3 +955,27 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
|
||||||
|
|
||||||
|
static volatile int64_t _totalExpectedItems = 0;
|
||||||
|
static volatile int64_t _totalMeasuredNodes = 0;
|
||||||
|
|
||||||
|
@implementation ASDataController (WorkMeasuring)
|
||||||
|
|
||||||
|
+ (void)_didLayoutNode
|
||||||
|
{
|
||||||
|
int64_t measured = OSAtomicIncrement64(&_totalMeasuredNodes);
|
||||||
|
int64_t expected = _totalExpectedItems;
|
||||||
|
if (measured % 20 == 0 || measured == expected) {
|
||||||
|
NSLog(@"Data controller avoided work (underestimated): %lld / %lld", measured, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)_expectToInsertNodes:(NSUInteger)count
|
||||||
|
{
|
||||||
|
OSAtomicAdd64((int64_t)count, &_totalExpectedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -29,4 +29,6 @@
|
|||||||
*/
|
*/
|
||||||
- (ASCellNode *)allocateNode;
|
- (ASCellNode *)allocateNode;
|
||||||
|
|
||||||
|
+ (NSArray<NSIndexPath *> *)indexPathsFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -48,4 +48,13 @@
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSArray<NSIndexPath *> *)indexPathsFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts
|
||||||
|
{
|
||||||
|
NSMutableArray *result = [NSMutableArray arrayWithCapacity:contexts.count];
|
||||||
|
for (ASIndexedNodeContext *ctx in contexts) {
|
||||||
|
[result addObject:ctx.indexPath];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -415,50 +415,44 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
|
|
||||||
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
|
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
|
||||||
{
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
[_delegate didBeginUpdatesInRangeController:self];
|
[_delegate didBeginUpdatesInRangeController:self];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||||
{
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
|
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
|
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASDisplayNodeAssertMainThread();
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Memory Management
|
#pragma mark - Memory Management
|
||||||
|
|||||||
@@ -53,16 +53,11 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCellNode *> *nodes, NS
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`.
|
* Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`.
|
||||||
*/
|
|
||||||
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes.
|
|
||||||
*
|
*
|
||||||
* @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance
|
* This method runs synchronously.
|
||||||
* to do so immediately on the main thread.
|
* @param batchCompletion A handler to be run after each batch is completed. It is executed synchronously on the calling thread.
|
||||||
*/
|
*/
|
||||||
- (void)layoutLoadedNodes:(NSArray<ASCellNode *> *)nodes fromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind;
|
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the size range for a specific node during the layout process.
|
* Provides the size range for a specific node during the layout process.
|
||||||
|
|||||||
Reference in New Issue
Block a user