From 965fe05c113f06cb346b7dee6aaa2e6da7fcd90b Mon Sep 17 00:00:00 2001 From: appleguy Date: Sat, 30 Jul 2016 18:04:08 -0700 Subject: [PATCH] [ASDisplayNode] Ensure that nil can never be returned from -measureWithSizeRange: (#2014) * [ASDisplayNode] Ensure that nil can never be returned from -measureWithSizeRange: This can happen in rare cases when multiple relayouts occur while a transition is being measured. * [ASDisplayNode] Use ternary operator style for nil check. --- AsyncDisplayKit/ASDisplayNode.mm | 6 +++++- AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index e4da5f69..48810ac2 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -642,7 +642,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { ASDN::MutexLocker l(__instanceLock__); if (! [self shouldMeasureWithSizeRange:constrainedSize]) { - return _calculatedLayout; + ASDisplayNodeAssertNotNil(_calculatedLayout, @"-[ASDisplayNode measureWithSizeRange:] _layout should not be nil! %@", self); + return _calculatedLayout ? : [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:CGSizeZero]; } [self cancelLayoutTransitionsInProgress]; @@ -659,6 +660,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _completePendingLayoutTransition]; } + ASDisplayNodeAssertNotNil(newLayout, @"-[ASDisplayNode measureWithSizeRange:] newLayout should not be nil! %@", self); return newLayout; } @@ -2071,6 +2073,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) layoutSpec.isMutable = NO; ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize]; + ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec); + // Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct. BOOL isFinalLayoutable = (layout.layoutableObject != self); if (isFinalLayoutable) { diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 9e143ae1..e0026fcb 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -28,7 +28,9 @@ static ASLayout *crossChildLayout(const id child, // stretched children will have a cross dimension of at least crossMin const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); - return [child measureWithSizeRange:childSizeRange]; + ASLayout *layout = [child measureWithSizeRange:childSizeRange]; + ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child); + return layout ? : [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:childSizeRange size:CGSizeZero]; } /**