mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-21 05:39:01 +00:00
[ASDisplayNode] Always layout nodes on a background thread (#1907)
* Always layout nodes on a background thread * Remove semaphore in ASDataController for allocating nodes and layout * Fix variable not used error * Remove overhead to create subarray of contexts of nodes while layout nodes * Remove extra allocation of allocatedNodes and indexPaths array
This commit is contained in:
parent
f95790f280
commit
a8c5ac138d
@ -791,7 +791,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO ihm: Can we always push the measure to the background thread and remove the parameter from the API?
|
// TODO ihm: Can we always push the measure to the background thread and remove the parameter from the API?
|
||||||
if (shouldMeasureAsync) {
|
if (ASDisplayNodeThreadIsMain()) {
|
||||||
ASPerformBlockOnBackgroundThread(transitionBlock);
|
ASPerformBlockOnBackgroundThread(transitionBlock);
|
||||||
} else {
|
} else {
|
||||||
transitionBlock();
|
transitionBlock();
|
||||||
|
|||||||
@ -165,18 +165,14 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For any given layout pass that occurs, this method will be called at least twice, once on the main thread and
|
// Layout node on whatever thread we are on. We handle the trampoline to the main thread in case the node is
|
||||||
// the background, to result in complete coverage of both loaded and unloaded nodes
|
// already loaded
|
||||||
BOOL isMainThread = ASDisplayNodeThreadIsMain();
|
|
||||||
for (NSUInteger k = range.location; k < NSMaxRange(range); k++) {
|
for (NSUInteger k = range.location; k < NSMaxRange(range); k++) {
|
||||||
ASCellNode *node = nodes[k];
|
ASCellNode *node = nodes[k];
|
||||||
// Only nodes that are loaded should be layout on the main thread
|
|
||||||
if (node.isNodeLoaded == isMainThread) {
|
|
||||||
ASIndexedNodeContext *context = contexts[k];
|
ASIndexedNodeContext *context = contexts[k];
|
||||||
[self _layoutNode:node withConstrainedSize:context.constrainedSize];
|
[self _layoutNode:node withConstrainedSize:context.constrainedSize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
- (void)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
@ -187,18 +183,12 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSUInteger nodeCount = contexts.count;
|
NSUInteger nodeCount = contexts.count;
|
||||||
NSMutableArray<ASCellNode *> *allocatedNodes = [NSMutableArray<ASCellNode *> arrayWithCapacity:nodeCount];
|
__strong NSIndexPath **allocatedContextIndexPaths = (__strong NSIndexPath **)calloc(nodeCount, sizeof(NSIndexPath *));
|
||||||
dispatch_group_t layoutGroup = dispatch_group_create();
|
__strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(nodeCount, sizeof(ASCellNode *));
|
||||||
|
|
||||||
for (NSUInteger j = 0; j < nodeCount; j += kASDataControllerSizingCountPerProcessor) {
|
for (NSUInteger j = 0; j < nodeCount; j += kASDataControllerSizingCountPerProcessor) {
|
||||||
NSInteger batchCount = MIN(kASDataControllerSizingCountPerProcessor, nodeCount - j);
|
NSInteger batchCount = MIN(kASDataControllerSizingCountPerProcessor, nodeCount - j);
|
||||||
|
|
||||||
// Allocate nodes concurrently.
|
|
||||||
__block NSArray *subarrayOfContexts;
|
|
||||||
__block NSArray *subarrayOfNodes;
|
|
||||||
dispatch_block_t allocationBlock = ^{
|
|
||||||
__strong ASIndexedNodeContext **allocatedContextBuffer = (__strong ASIndexedNodeContext **)calloc(batchCount, sizeof(ASIndexedNodeContext *));
|
|
||||||
__strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(batchCount, sizeof(ASCellNode *));
|
|
||||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||||
dispatch_apply(batchCount, queue, ^(size_t i) {
|
dispatch_apply(batchCount, queue, ^(size_t i) {
|
||||||
unsigned long k = j + i;
|
unsigned long k = j + i;
|
||||||
@ -208,57 +198,27 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
|||||||
ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource);
|
ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource);
|
||||||
node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps.
|
node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps.
|
||||||
}
|
}
|
||||||
allocatedNodeBuffer[i] = node;
|
|
||||||
allocatedContextBuffer[i] = context;
|
allocatedContextIndexPaths[k] = context.indexPath;
|
||||||
|
allocatedNodeBuffer[k] = node;
|
||||||
|
|
||||||
|
[self _layoutNode:node withConstrainedSize:context.constrainedSize];
|
||||||
});
|
});
|
||||||
subarrayOfNodes = [NSArray arrayWithObjects:allocatedNodeBuffer count:batchCount];
|
}
|
||||||
subarrayOfContexts = [NSArray arrayWithObjects:allocatedContextBuffer count:batchCount];
|
|
||||||
|
// Create nodes and indexPaths array's
|
||||||
|
NSArray *allocatedNodes = [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 < batchCount; i++) {
|
for (int i = 0; i < nodeCount; i++) {
|
||||||
allocatedContextBuffer[i] = nil;
|
allocatedContextIndexPaths[i] = nil;
|
||||||
allocatedNodeBuffer[i] = nil;
|
allocatedNodeBuffer[i] = nil;
|
||||||
}
|
}
|
||||||
free(allocatedContextBuffer);
|
free(allocatedContextIndexPaths);
|
||||||
free(allocatedNodeBuffer);
|
free(allocatedNodeBuffer);
|
||||||
};
|
|
||||||
|
|
||||||
// Run the allocation block to concurrently create the cell nodes. Then, handle layout for nodes that are already loaded
|
|
||||||
// (e.g. the dataSource may have provided cells that have been used before), which must do layout on the main thread.
|
|
||||||
NSRange batchRange = NSMakeRange(0, batchCount);
|
|
||||||
if (ASDisplayNodeThreadIsMain()) {
|
|
||||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
allocationBlock();
|
|
||||||
dispatch_semaphore_signal(sema);
|
|
||||||
});
|
|
||||||
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
|
|
||||||
|
|
||||||
[self _layoutNodes:subarrayOfNodes fromContexts:subarrayOfContexts atIndexesOfRange:batchRange ofKind:kind];
|
|
||||||
} else {
|
|
||||||
allocationBlock();
|
|
||||||
[_mainSerialQueue performBlockOnMainThread:^{
|
|
||||||
[self _layoutNodes:subarrayOfNodes fromContexts:subarrayOfContexts atIndexesOfRange:batchRange ofKind:kind];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
[allocatedNodes addObjectsFromArray:subarrayOfNodes];
|
|
||||||
|
|
||||||
dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
// We should already have measured loaded nodes before we left the main thread. Layout the remaining ones on a background thread.
|
|
||||||
NSRange asyncBatchRange = NSMakeRange(j, batchCount);
|
|
||||||
[self _layoutNodes:allocatedNodes fromContexts:contexts atIndexesOfRange:asyncBatchRange ofKind:kind];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block the _editingTransactionQueue from executing a new edit transaction until layout is done & _editingNodes array is updated.
|
|
||||||
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
|
|
||||||
|
|
||||||
if (completionBlock) {
|
if (completionBlock) {
|
||||||
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:nodeCount];
|
|
||||||
for (ASIndexedNodeContext *context in contexts) {
|
|
||||||
[indexPaths addObject:context.indexPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
completionBlock(allocatedNodes, indexPaths);
|
completionBlock(allocatedNodes, indexPaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user