Use a sentinel NSUInteger for node layout data #trivial (#428)

* Use a sentinel NSUInteger for node layout data

* Add a comment

* Address feedback from @appleguy
This commit is contained in:
Adlai Holler
2017-07-10 05:10:04 -07:00
committed by Huy Nguyen
parent bc361caf5e
commit 1fa498873e
5 changed files with 31 additions and 54 deletions

View File

@@ -82,13 +82,11 @@
} }
ASLayout *layout = nil; ASLayout *layout = nil;
if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) { NSUInteger version = _layoutVersion;
if (_calculatedDisplayNodeLayout->isValid(constrainedSize, parentSize, version)) {
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self); ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
// Our calculated layout is suitable for this constrainedSize, so keep using it and
// invalidate any pending layout that has been generated in the past.
_pendingDisplayNodeLayout = nullptr;
layout = _calculatedDisplayNodeLayout->layout; layout = _calculatedDisplayNodeLayout->layout;
} else if (_pendingDisplayNodeLayout != nullptr && _pendingDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) { } else if (_pendingDisplayNodeLayout != nullptr && _pendingDisplayNodeLayout->isValid(constrainedSize, parentSize, version)) {
ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self); ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self);
layout = _pendingDisplayNodeLayout->layout; layout = _pendingDisplayNodeLayout->layout;
} else { } else {
@@ -96,7 +94,7 @@
layout = [self calculateLayoutThatFits:constrainedSize layout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:self.style.size restrictedToSize:self.style.size
relativeToParentSize:parentSize]; relativeToParentSize:parentSize];
_pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(layout, constrainedSize, parentSize); _pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(layout, constrainedSize, parentSize, version);
ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self); ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self);
} }
@@ -308,7 +306,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
// Prefer _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout (if exists, it's the newest) // Prefer _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout (if exists, it's the newest)
// If there is no _pending, check if _calculated is valid to reuse (avoiding recalculation below). // If there is no _pending, check if _calculated is valid to reuse (avoiding recalculation below).
if (_pendingDisplayNodeLayout == nullptr) { if (_pendingDisplayNodeLayout == nullptr) {
if (_calculatedDisplayNodeLayout->isDirty() == NO if (_calculatedDisplayNodeLayout->version >= _layoutVersion
&& (_calculatedDisplayNodeLayout->requestedLayoutFromAbove == YES && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove == YES
|| CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) { || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) {
return; return;
@@ -337,8 +335,8 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
BOOL pendingLayoutApplicable = NO; BOOL pendingLayoutApplicable = NO;
if (nextLayout == nullptr) { if (nextLayout == nullptr) {
as_log_verbose(ASLayoutLog(), "No pending layout."); as_log_verbose(ASLayoutLog(), "No pending layout.");
} else if (nextLayout->isDirty()) { } else if (nextLayout->version < _layoutVersion) {
as_log_verbose(ASLayoutLog(), "Pending layout is invalid."); as_log_verbose(ASLayoutLog(), "Pending layout is stale.");
} else if (layoutSizeDifferentFromBounds) { } else if (layoutSizeDifferentFromBounds) {
as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size)); as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size));
} else { } else {
@@ -349,12 +347,13 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
if (!pendingLayoutApplicable) { if (!pendingLayoutApplicable) {
as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size."); as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size.");
// Use the last known constrainedSize passed from a parent during layout (if never, use bounds). // Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
NSUInteger version = _layoutVersion;
ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass]; ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
ASLayout *layout = [self calculateLayoutThatFits:constrainedSize ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:self.style.size restrictedToSize:self.style.size
relativeToParentSize:boundsSizeForLayout]; relativeToParentSize:boundsSizeForLayout];
nextLayout = std::make_shared<ASDisplayNodeLayout>(layout, constrainedSize, boundsSizeForLayout); nextLayout = std::make_shared<ASDisplayNodeLayout>(layout, constrainedSize, boundsSizeForLayout, version);
} }
if (didCreateNewContext) { if (didCreateNewContext) {
@@ -425,7 +424,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
ASLayout *layout; ASLayout *layout;
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
if (_calculatedDisplayNodeLayout->isDirty()) { if (_calculatedDisplayNodeLayout->version < _layoutVersion) {
return; return;
} }
layout = _calculatedDisplayNodeLayout->layout; layout = _calculatedDisplayNodeLayout->layout;
@@ -569,6 +568,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
} }
// Perform a full layout creation pass with passed in constrained size to create the new layout for the transition // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition
NSUInteger newLayoutVersion = _layoutVersion;
ASLayout *newLayout; ASLayout *newLayout;
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
@@ -609,7 +609,8 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
auto previousLayout = _calculatedDisplayNodeLayout; auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(newLayout, auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(newLayout,
constrainedSize, constrainedSize,
constrainedSize.max); constrainedSize.max,
newLayoutVersion);
[self _locked_setCalculatedDisplayNodeLayout:pendingLayout]; [self _locked_setCalculatedDisplayNodeLayout:pendingLayout];
// Setup pending layout transition for animation // Setup pending layout transition for animation

View File

@@ -285,6 +285,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_calculatedDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(); _calculatedDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>();
_pendingDisplayNodeLayout = nullptr; _pendingDisplayNodeLayout = nullptr;
_layoutVersion = 1;
_defaultLayoutTransitionDuration = 0.2; _defaultLayoutTransitionDuration = 0.2;
_defaultLayoutTransitionDelay = 0.0; _defaultLayoutTransitionDelay = 0.0;
@@ -882,12 +883,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
// This will cause the next layout pass to compute a new layout instead of returning _layoutVersion++;
// the cached layout in case the constrained or parent size did not change
_calculatedDisplayNodeLayout->invalidate();
if (_pendingDisplayNodeLayout != nullptr) {
_pendingDisplayNodeLayout->invalidate();
}
_unflattenedLayout = nil; _unflattenedLayout = nil;
@@ -924,7 +920,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// This method will confirm that the layout is up to date (and update if needed). // This method will confirm that the layout is up to date (and update if needed).
// Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning). // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
[self _locked_measureNodeWithBoundsIfNecessary:bounds]; [self _locked_measureNodeWithBoundsIfNecessary:bounds];
_pendingDisplayNodeLayout = nullptr;
[self _locked_layoutPlaceholderIfNecessary]; [self _locked_layoutPlaceholderIfNecessary];
} }

View File

@@ -34,7 +34,6 @@ NS_ASSUME_NONNULL_BEGIN
@protocol _ASDisplayLayerDelegate; @protocol _ASDisplayLayerDelegate;
@class _ASDisplayLayer; @class _ASDisplayLayer;
@class _ASPendingState; @class _ASPendingState;
@class ASSentinel;
struct ASDisplayNodeFlags; struct ASDisplayNodeFlags;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
@@ -160,6 +159,10 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
std::shared_ptr<ASDisplayNodeLayout> _calculatedDisplayNodeLayout; std::shared_ptr<ASDisplayNodeLayout> _calculatedDisplayNodeLayout;
std::shared_ptr<ASDisplayNodeLayout> _pendingDisplayNodeLayout; std::shared_ptr<ASDisplayNodeLayout> _pendingDisplayNodeLayout;
/// Sentinel for layout data. Incremented when we get -setNeedsLayout / -invalidateCalculatedLayout.
/// Starts at 1.
std::atomic<NSUInteger> _layoutVersion;
ASDisplayNodeViewBlock _viewBlock; ASDisplayNodeViewBlock _viewBlock;
ASDisplayNodeLayerBlock _layerBlock; ASDisplayNodeLayerBlock _layerBlock;
NSMutableArray<ASDisplayNodeDidLoadBlock> *_onDidLoadBlocks; NSMutableArray<ASDisplayNodeDidLoadBlock> *_onDidLoadBlocks;

View File

@@ -30,35 +30,26 @@ struct ASDisplayNodeLayout {
ASSizeRange constrainedSize; ASSizeRange constrainedSize;
CGSize parentSize; CGSize parentSize;
BOOL requestedLayoutFromAbove; BOOL requestedLayoutFromAbove;
BOOL _dirty; NSUInteger version;
/* /*
* Create a new display node layout with * Create a new display node layout with
* @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize: * @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize:
* @param constrainedSize Constrained size used to create the layout * @param constrainedSize Constrained size used to create the layout
* @param parentSize Parent size used to create the layout * @param parentSize Parent size used to create the layout
* @param version The version of the source layout data see ASDisplayNode's _layoutVersion.
*/ */
ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize) ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version)
: layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), _dirty(NO) {}; : layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), version(version) {};
/* /*
* Creates a layout without any layout associated. By default this display node layout is dirty. * Creates a layout without any layout associated. By default this display node layout is dirty.
*/ */
ASDisplayNodeLayout() ASDisplayNodeLayout()
: layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), _dirty(YES) {}; : layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), version(0) {};
/** /**
* Returns if the display node layout is dirty as it was invalidated or it was created without a layout. * Returns whether this is valid for a given constrained size, parent size, and version
*/ */
BOOL isDirty(); BOOL isValid(ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version);
/**
* Returns if ASDisplayNode is still valid for a given constrained and parent size
*/
BOOL isValidForConstrainedSizeParentSize(ASSizeRange constrainedSize, CGSize parentSize);
/**
* Invalidate the display node layout
*/
void invalidate();
}; };

View File

@@ -17,23 +17,10 @@
#import <AsyncDisplayKit/ASDisplayNodeLayout.h> #import <AsyncDisplayKit/ASDisplayNodeLayout.h>
BOOL ASDisplayNodeLayout::isDirty() BOOL ASDisplayNodeLayout::isValid(ASSizeRange theConstrainedSize, CGSize theParentSize, NSUInteger versionArg)
{ {
return _dirty || layout == nil; return version >= versionArg
} && layout != nil
&& CGSizeEqualToSize(parentSize, theParentSize)
BOOL ASDisplayNodeLayout::isValidForConstrainedSizeParentSize(ASSizeRange theConstrainedSize, CGSize theParentSize) && ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize);
{
// Only generate a new layout if:
// - The current layout is dirty
// - The passed constrained size is different than the original layout's parent or constrained size
return (layout != nil
&& _dirty == NO
&& CGSizeEqualToSize(parentSize, theParentSize)
&& ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize));
}
void ASDisplayNodeLayout::invalidate()
{
_dirty = YES;
} }