diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index 6c2299da8..4c75d2336 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -217,8 +217,7 @@ extern NSInteger const ASDefaultDrawingPriority; * * @return NO if the node wraps a _ASDisplayView, YES otherwise. */ -@property (nonatomic, readonly, assign, getter=isSynchronous) BOOL synchronous; - +@property (atomic, readonly, assign, getter=isSynchronous) BOOL synchronous; /** @name Getting view and layer */ diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index ef5fa5ffe..7dbb1df19 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -100,9 +100,9 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties // like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only. -BOOL ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(ASDisplayNodeFlags flags) +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked) { - return flags.synchronous && !flags.layerBacked; + return isSynchronous && !isLayerBacked; } _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node) @@ -314,7 +314,7 @@ - (instancetype)initWithViewClass:(Class)viewClass ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView"); _viewClass = viewClass; - _flags.synchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]]; + setFlag(Synchronous, ![viewClass isSubclassOfClass:[_ASDisplayView class]]); return self; } @@ -328,8 +328,8 @@ - (instancetype)initWithLayerClass:(Class)layerClass ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer"); _layerClass = layerClass; - _flags.synchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]; _flags.layerBacked = YES; + setFlag(Synchronous, ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]); return self; } @@ -378,7 +378,7 @@ - (void)setViewBlock:(ASDisplayNodeViewBlock)viewBlock ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView"); _viewBlock = viewBlock; - _flags.synchronous = YES; + setFlag(Synchronous, YES); } - (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock @@ -387,8 +387,8 @@ - (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer"); _layerBlock = layerBlock; - _flags.synchronous = YES; _flags.layerBacked = YES; + setFlag(Synchronous, YES); } - (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body @@ -411,7 +411,7 @@ - (void)dealloc _flags.isDeallocating = YES; // Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes. - ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self); + ASDisplayNodeAssert(checkFlag(Synchronous) || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self); self.asyncLayer.asyncDelegate = nil; _view.asyncdisplaykit_node = nil; @@ -547,7 +547,7 @@ - (void)__unloadNode { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert([self isNodeLoaded], @"Implementation shouldn't call __unloadNode if not loaded: %@", self); - ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self); + ASDisplayNodeAssert(checkFlag(Synchronous) == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self); ASDN::MutexLocker l(__instanceLock__); if (_flags.layerBacked) { @@ -583,7 +583,7 @@ - (UIView *)_locked_viewToLoad } // Special handling of wrapping UIKit components - if (_flags.synchronous) { + if (checkFlag(Synchronous)) { // UIImageView layers. More details on the flags if ([_viewClass isSubclassOfClass:[UIImageView class]]) { _flags.canClearContentsOfLayer = NO; @@ -805,8 +805,7 @@ - (_ASDisplayLayer *)_locked_asyncLayer - (BOOL)isSynchronous { - ASDN::MutexLocker l(__instanceLock__); - return _flags.synchronous; + return checkFlag(Synchronous); } - (void)setLayerBacked:(BOOL)isLayerBacked @@ -836,7 +835,7 @@ - (BOOL)isLayerBacked - (BOOL)supportsLayerBacking { ASDN::MutexLocker l(__instanceLock__); - return !_flags.synchronous && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class]; + return !checkFlag(Synchronous) && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class]; } - (BOOL)shouldAnimateSizeChanges @@ -1948,7 +1947,7 @@ - (BOOL)displaysAsynchronously */ - (BOOL)_locked_displaysAsynchronously { - return _flags.synchronous == NO && _flags.displaysAsynchronously; + return checkFlag(Synchronous) == NO && _flags.displaysAsynchronously; } - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously @@ -1958,7 +1957,7 @@ - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously ASDN::MutexLocker l(__instanceLock__); // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) - if (_flags.synchronous) { + if (checkFlag(Synchronous)) { return; } @@ -2052,7 +2051,7 @@ - (void)setContentsScaleForDisplay:(CGFloat)contentsScaleForDisplay - (void)displayImmediately { ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only"); + ASDisplayNodeAssert(!checkFlag(Synchronous), @"this method is designed for asynchronous mode only"); [self.asyncLayer displayImmediately]; } @@ -2073,7 +2072,7 @@ - (void)__setNeedsDisplay BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState); // FIXME: This should not need to recursively display, so create a non-recursive variant. // The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive. - if (_layer != nil && !_flags.synchronous && nowDisplay && [self _implementsDisplay]) { + if (_layer != nil && !checkFlag(Synchronous) && nowDisplay && [self _implementsDisplay]) { shouldScheduleForDisplay = YES; } } @@ -2317,7 +2316,7 @@ - (void)setDisplaySuspended:(BOOL)flag __instanceLock__.lock(); // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) - if (_flags.synchronous || _flags.displaySuspended == flag) { + if (checkFlag(Synchronous) || _flags.displaySuspended == flag) { __instanceLock__.unlock(); return; } @@ -3406,7 +3405,7 @@ - (void)setHierarchyState:(ASHierarchyState)newState // Entered rasterization state. if (newState & ASHierarchyStateRasterized) { - ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with shouldRasterizeDescendants=YES. Node: %@", self); + ASDisplayNodeAssert(checkFlag(Synchronous) == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with shouldRasterizeDescendants=YES. Node: %@", self); } // Entered or exited range managed state. @@ -3907,7 +3906,7 @@ - (void)_locked_applyPendingViewState if (_flags.layerBacked) { [_pendingViewState applyToLayer:self.layer]; } else { - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(_flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked); [_pendingViewState applyToView:self.view withSpecialPropertiesHandling:specialPropertiesHandling]; } diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 73322a343..fc4b3c4ea 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -241,7 +241,7 @@ - (void)setFrame:(CGRect)rect // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: struct ASDisplayNodeFlags flags = _flags; - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), flags.layerBacked); BOOL nodeLoaded = __loaded(self); BOOL isMainThread = ASDisplayNodeThreadIsMain(); @@ -635,7 +635,7 @@ - (void)setBackgroundColor:(UIColor *)newBackgroundColor if (shouldApply) { CGColorRef oldBackgroundCGColor = _layer.backgroundColor; - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(_flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked); if (specialPropertiesHandling) { _view.backgroundColor = newBackgroundColor; } else { diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index ed973e647..2535c9c86 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN struct ASDisplayNodeFlags; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); -BOOL ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(ASDisplayNodeFlags flags); +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked); /// Get the pending view state for the node, creating one if needed. _ASPendingState * ASDisplayNodeGetPendingState(ASDisplayNode * node); @@ -55,6 +55,16 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) ASDisplayNodeMethodOverrideClearFetchedData = 1 << 6 }; +typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags) +{ + Synchronous = 1 << 0, +}; + +#define checkFlag(flag) ((_atomicFlags.load() & flag) != 0) +// Returns the old value of the flag as a BOOL. +#define setFlag(flag, x) (((x ? _atomicFlags.fetch_or(flag) \ + : _atomicFlags.fetch_and(~flag)) & flag) != 0) + FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp; @@ -71,9 +81,10 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo UIView *_view; CALayer *_layer; + std::atomic _atomicFlags; + struct ASDisplayNodeFlags { // public properties - unsigned synchronous:1; unsigned viewEverHadAGestureRecognizerAttached:1; unsigned layerBacked:1; unsigned displaysAsynchronously:1;