From 7d902d98c9421cd35251d67981d74c749df31437 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 13 May 2016 21:02:11 +0200 Subject: [PATCH 1/2] Fix passing wrong constrained size to nodes In _layoutNodesFromContexts:ofKind:completion: we pass the full array of contexts to _layoutNodes:fromContexts:atIndexesOfRange:ofKind: but for nodes we pass a subarray of nodes instead based on the batchCount. As batchRange we always start from 0 to batchCount. We now use the same indexes that we use to create the subarray of nodes to get a subarray of contexts that we pass to _layoutNodes:fromContexts:atIndexesOfRange:ofKind:. --- AsyncDisplayKit/Details/ASDataController.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 3271ebb5..cbe6e763 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -191,14 +191,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger j = 0; j < nodeCount; j += kASDataControllerSizingCountPerProcessor) { NSInteger batchCount = MIN(kASDataControllerSizingCountPerProcessor, nodeCount - j); - __block NSArray *subarray; + // Get subcontexts for the range of nodes that will be allocated + NSArray *subcontexts = [contexts subarrayWithRange:NSMakeRange(j, batchCount)]; + // Allocate nodes concurrently. + __block NSArray *subarray; dispatch_block_t allocationBlock = ^{ __strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(batchCount, sizeof(ASCellNode *)); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(batchCount, queue, ^(size_t i) { - unsigned long k = j + i; - ASCellNode *node = [contexts[k] allocateNode]; + ASCellNode *node = [subcontexts[i] allocateNode]; if (node == nil) { ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource); node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps. @@ -225,11 +227,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - [self _layoutNodes:subarray fromContexts:contexts atIndexesOfRange:batchRange ofKind:kind]; + [self _layoutNodes:subarray fromContexts:subcontexts atIndexesOfRange:batchRange ofKind:kind]; } else { allocationBlock(); [_mainSerialQueue performBlockOnMainThread:^{ - [self _layoutNodes:subarray fromContexts:contexts atIndexesOfRange:batchRange ofKind:kind]; + [self _layoutNodes:subarray fromContexts:subcontexts atIndexesOfRange:batchRange ofKind:kind]; }]; } From 33b76bbce0991e43055b91c63e7982c1472a222c Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 13 May 2016 21:38:25 +0200 Subject: [PATCH 2/2] =?UTF-8?q?Use=20calloc=E2=80=99d=20array=20for=20suba?= =?UTF-8?q?rray=20of=20contexts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsyncDisplayKit/Details/ASDataController.mm | 26 ++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index cbe6e763..2847abf6 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -190,29 +190,33 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger j = 0; j < nodeCount; j += kASDataControllerSizingCountPerProcessor) { NSInteger batchCount = MIN(kASDataControllerSizingCountPerProcessor, nodeCount - j); - - // Get subcontexts for the range of nodes that will be allocated - NSArray *subcontexts = [contexts subarrayWithRange:NSMakeRange(j, batchCount)]; - + // Allocate nodes concurrently. - __block NSArray *subarray; + __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_apply(batchCount, queue, ^(size_t i) { - ASCellNode *node = [subcontexts[i] allocateNode]; + unsigned long k = j + i; + ASIndexedNodeContext *context = contexts[k]; + ASCellNode *node = [context allocateNode]; if (node == nil) { ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource); node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps. } allocatedNodeBuffer[i] = node; + allocatedContextBuffer[i] = context; }); - subarray = [[NSArray alloc] initWithObjects:allocatedNodeBuffer count:batchCount]; - + subarrayOfNodes = [[NSArray alloc] initWithObjects:allocatedNodeBuffer count:batchCount]; + subarrayOfContexts = [NSArray arrayWithObjects:allocatedContextBuffer count:batchCount]; // Nil out buffer indexes to allow arc to free the stored cells. for (int i = 0; i < batchCount; i++) { + allocatedContextBuffer[i] = nil; allocatedNodeBuffer[i] = nil; } + free(allocatedContextBuffer); free(allocatedNodeBuffer); }; @@ -227,15 +231,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - [self _layoutNodes:subarray fromContexts:subcontexts atIndexesOfRange:batchRange ofKind:kind]; + [self _layoutNodes:subarrayOfNodes fromContexts:subarrayOfContexts atIndexesOfRange:batchRange ofKind:kind]; } else { allocationBlock(); [_mainSerialQueue performBlockOnMainThread:^{ - [self _layoutNodes:subarray fromContexts:subcontexts atIndexesOfRange:batchRange ofKind:kind]; + [self _layoutNodes:subarrayOfNodes fromContexts:subarrayOfContexts atIndexesOfRange:batchRange ofKind:kind]; }]; } - [allocatedNodes addObjectsFromArray:subarray]; + [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.