From 254f55b7583f393895cd39adb87af44fa4f65d44 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 29 Nov 2015 14:57:43 -0800 Subject: [PATCH] Add locking for ASInterfaceState. Misc. cleanup. --- AsyncDisplayKit/ASDisplayNode.mm | 66 +++---------------- AsyncDisplayKit/ASDisplayNodeExtras.h | 17 ++++- AsyncDisplayKit/ASDisplayNodeExtras.mm | 45 ++++++++++++- .../Private/ASDisplayNodeInternal.h | 1 - .../Sample.xcodeproj/project.pbxproj | 4 +- 5 files changed, 68 insertions(+), 65 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 298e683954..9c52f08c21 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -379,11 +379,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return YES; } -- (void)__exitedHierarchy -{ - -} - - (UIView *)_viewToLoad { UIView *view; @@ -787,55 +782,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return transform; } -static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possibleAncestor, ASDisplayNode *possibleDescendent) -{ - ASDisplayNode *supernode = possibleDescendent; - while (supernode) { - if (supernode == possibleAncestor) { - return YES; - } - supernode = supernode.supernode; - } - - return NO; -} - -/** - * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is - * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined - * failure mode (i.e. returning CGPointZero or returning the point exactly as passed in). Rather than track the internal - * undefined and undocumented behavior of UIKit in ASDisplayNode, this operation is defined to be incorrect in all - * circumstances and must be fixed wherever encountered. - */ -static inline ASDisplayNode *_ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) -{ - ASDisplayNode *possibleAncestor = node1; - while (possibleAncestor) { - if (_ASDisplayNodeIsAncestorOfDisplayNode(possibleAncestor, node2)) { - break; - } - possibleAncestor = possibleAncestor.supernode; - } - - ASDisplayNodeCAssertNotNil(possibleAncestor, @"Could not find a common ancestor between node1: %@ and node2: %@", node1, node2); - return possibleAncestor; -} - -static inline ASDisplayNode *_getRootNode(ASDisplayNode *node) -{ - // node <- supernode on each loop - // previous <- node on each loop where node is not nil - // previous is the final non-nil value of supernode, i.e. the root node - ASDisplayNode *previousNode = node; - while ((node = [node supernode])) { - previousNode = node; - } - return previousNode; -} - static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNode *referenceNode, ASDisplayNode *targetNode) { - ASDisplayNode *ancestor = _ASDisplayNodeFindClosestCommonAncestor(referenceNode, targetNode); + ASDisplayNode *ancestor = ASDisplayNodeFindClosestCommonAncestor(referenceNode, targetNode); // Transform into global (away from reference coordinate space) CATransform3D transformToGlobal = [referenceNode _transformToAncestor:ancestor]; @@ -850,7 +799,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo { ASDisplayNodeAssertThreadAffinity(self); // Get root node of the accessible node hierarchy, if node not specified - node = node ? node : _getRootNode(self); + node = node ? node : ASDisplayNodeUltimateParentOfNode(self); // Calculate transform to map points between coordinate spaces CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self); @@ -865,7 +814,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo { ASDisplayNodeAssertThreadAffinity(self); // Get root node of the accessible node hierarchy, if node not specified - node = node ? node : _getRootNode(self); + node = node ? node : ASDisplayNodeUltimateParentOfNode(self); // Calculate transform to map points between coordinate spaces CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node); @@ -880,7 +829,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo { ASDisplayNodeAssertThreadAffinity(self); // Get root node of the accessible node hierarchy, if node not specified - node = node ? node : _getRootNode(self); + node = node ? node : ASDisplayNodeUltimateParentOfNode(self); // Calculate transform to map points between coordinate spaces CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self); @@ -895,7 +844,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo { ASDisplayNodeAssertThreadAffinity(self); // Get root node of the accessible node hierarchy, if node not specified - node = node ? node : _getRootNode(self); + node = node ? node : ASDisplayNodeUltimateParentOfNode(self); // Calculate transform to map points between coordinate spaces CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node); @@ -1614,6 +1563,7 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer) - (void)__didLoad { + ASDN::MutexLocker l(_propertyLock); if (_nodeLoadedBlock) { _nodeLoadedBlock(self); _nodeLoadedBlock = nil; @@ -1638,8 +1588,6 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer) ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode"); ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); - - [self __exitedHierarchy]; } - (void)clearContents @@ -1689,11 +1637,13 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer) - (ASInterfaceState)interfaceState { + ASDN::MutexLocker l(_propertyLock); return _interfaceState; } - (void)setInterfaceState:(ASInterfaceState)interfaceState { + ASDN::MutexLocker l(_propertyLock); if (interfaceState != _interfaceState) { if ((interfaceState & ASInterfaceStateMeasureLayout) != (_interfaceState & ASInterfaceStateMeasureLayout)) { // Trigger asynchronous measurement if it is not already cached or being calculated. diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.h b/AsyncDisplayKit/ASDisplayNodeExtras.h index ca12de19a8..4ccf0b48ca 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.h +++ b/AsyncDisplayKit/ASDisplayNodeExtras.h @@ -24,13 +24,18 @@ extern ASDisplayNode *ASLayerToDisplayNode(CALayer *layer); */ extern ASDisplayNode *ASViewToDisplayNode(UIView *view); +/** + Given a node, returns the root of the node heirarchy (where supernode == nil) + */ +extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node); + /** This function will walk the layer heirarchy, spanning discontinuous sections of the node heirarchy (e.g. the layers of UIKit intermediate views in UIViewControllers, UITableView, UICollectionView). In the event that a node's backing layer is not created yet, the function will only walk the direct subnodes instead of forcing the layer heirarchy to be created. */ -void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node)); +extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node)); /** Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block. @@ -42,6 +47,16 @@ extern id ASDisplayNodeFind(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *no */ extern id ASDisplayNodeFindClass(ASDisplayNode *start, Class c); +/** + * Given two nodes, finds their most immediate common parent. Used for geometry conversion methods. + * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is + * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined + * failure mode (i.e. returning CGPointZero or returning the point exactly as passed in). Rather than track the internal + * undefined and undocumented behavior of UIKit in ASDisplayNode, this operation is defined to be incorrect in all + * circumstances and must be fixed wherever encountered. + */ +extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2); + /** Given a display node, collects all descendents. This is a specialization of ASCollectContainer() that walks the Core Animation layer tree as opposed to the display node tree, thus supporting non-continues display node hierarchies. */ diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.mm b/AsyncDisplayKit/ASDisplayNodeExtras.mm index 721e536785..681640ffa3 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.mm +++ b/AsyncDisplayKit/ASDisplayNodeExtras.mm @@ -10,17 +10,17 @@ #import "ASDisplayNodeInternal.h" -inline ASDisplayNode *ASLayerToDisplayNode(CALayer *layer) +extern ASDisplayNode *ASLayerToDisplayNode(CALayer *layer) { return layer.asyncdisplaykit_node; } -inline ASDisplayNode *ASViewToDisplayNode(UIView *view) +extern ASDisplayNode *ASViewToDisplayNode(UIView *view) { return view.asyncdisplaykit_node; } -void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node)) +extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node)) { if (!node) { ASDisplayNodeCAssertNotNil(layer, @"Cannot recursively perform with nil node and nil layer"); @@ -148,6 +148,45 @@ extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) }); } +static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possibleAncestor, ASDisplayNode *possibleDescendent) +{ + ASDisplayNode *supernode = possibleDescendent; + while (supernode) { + if (supernode == possibleAncestor) { + return YES; + } + supernode = supernode.supernode; + } + + return NO; +} + +extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) +{ + ASDisplayNode *possibleAncestor = node1; + while (possibleAncestor) { + if (_ASDisplayNodeIsAncestorOfDisplayNode(possibleAncestor, node2)) { + break; + } + possibleAncestor = possibleAncestor.supernode; + } + + ASDisplayNodeCAssertNotNil(possibleAncestor, @"Could not find a common ancestor between node1: %@ and node2: %@", node1, node2); + return possibleAncestor; +} + +extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) +{ + // node <- supernode on each loop + // previous <- node on each loop where node is not nil + // previous is the final non-nil value of supernode, i.e. the root node + ASDisplayNode *previousNode = node; + while ((node = [node supernode])) { + previousNode = node; + } + return previousNode; +} + #pragma mark - Placeholders UIColor *ASDisplayNodeDefaultPlaceholderColor() diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 156e6f7730..0d3ab7efe3 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -125,7 +125,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { // Swizzle to extend the builtin functionality with custom logic - (BOOL)__shouldLoadViewOrLayer; - (BOOL)__shouldSize; -- (void)__exitedHierarchy; // Core implementation of -measureWithSizeRange:. Must be called with _propertyLock held. - (ASLayout *)__measureWithSizeRange:(ASSizeRange)constrainedSize; diff --git a/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj b/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj index 038f1b0bf6..2be08f1dcd 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj +++ b/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj @@ -266,7 +266,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -301,7 +301,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES;