From 427ba55f9297d7530c02a8e0cfa74d8e7f158e56 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sun, 25 Mar 2018 10:46:04 -0700 Subject: [PATCH] Make objects conform to NSLocking (#851) * Make display node, layout spec, and style conform to NSLocking so that users/subclasses can access their locks * Update the changelog * Align slashes * Put it back, when we're in ASDisplayNode * Go a little further * Put back the changes I didn't mean to commit * Kick the CI * Fix yoga build * Put back non-locking change * Address comments from Scott --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 - CHANGELOG.md | 1 + Source/ASButtonNode.mm | 58 ++--- Source/ASControlNode.mm | 58 +++-- Source/ASDisplayNode+Layout.mm | 5 +- Source/ASDisplayNode+Yoga.mm | 6 +- Source/ASDisplayNode.h | 2 +- Source/ASDisplayNode.mm | 16 +- Source/ASImageNode+AnimatedImage.mm | 20 +- Source/ASImageNode.mm | 7 +- Source/ASMapNode.mm | 26 +-- Source/ASMultiplexImageNode.mm | 14 +- Source/ASNetworkImageNode.mm | 123 +++++----- Source/ASScrollNode.mm | 15 +- Source/ASTextNode.mm | 212 +++++------------- Source/ASTextNode2.mm | 120 +++++----- Source/ASVideoNode.mm | 104 +++++---- Source/ASVideoPlayerNode.mm | 27 +-- Source/Base/ASBaseDefines.h | 23 ++ Source/Details/ASThread.h | 72 +++++- Source/Layout/ASLayoutElement.h | 2 +- Source/Layout/ASLayoutElement.mm | 12 + Source/Layout/ASLayoutSpec.h | 2 +- Source/Layout/ASLayoutSpec.mm | 12 + Source/Private/ASDisplayNode+AsyncDisplay.mm | 2 +- .../Private/ASDisplayNode+FrameworkPrivate.h | 2 +- .../ASDisplayNode+FrameworkSubclasses.h | 40 ---- Source/Private/ASDisplayNode+UIViewBridge.mm | 1 - Source/Private/ASDisplayNodeInternal.h | 2 + 29 files changed, 461 insertions(+), 527 deletions(-) delete mode 100644 Source/Private/ASDisplayNode+FrameworkSubclasses.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index e50e895c4..be2a0441f 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -424,7 +424,6 @@ DBDB83971C6E879900D0098C /* ASPagerFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = DBDB83931C6E879900D0098C /* ASPagerFlowLayout.m */; }; DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */; settings = {ATTRIBUTES = (Private, ); }; }; DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; - DE7EF4F81DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = DE7EF4F71DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h */; settings = {ATTRIBUTES = (Private, ); }; }; DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */; }; DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -933,7 +932,6 @@ DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPagerFlowLayout.h; sourceTree = ""; }; DBDB83931C6E879900D0098C /* ASPagerFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPagerFlowLayout.m; sourceTree = ""; }; DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = ""; }; - DE7EF4F71DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkSubclasses.h"; sourceTree = ""; }; DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDelegateProxy.h; sourceTree = ""; }; DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = ""; }; DEC146B41C37A16A004A0EE7 /* ASCollectionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCollectionInternal.h; path = Details/ASCollectionInternal.h; sourceTree = ""; }; @@ -1421,7 +1419,6 @@ 058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */, 058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */, DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */, - DE7EF4F71DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h */, 058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */, 058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */, 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */, @@ -1849,7 +1846,6 @@ 34EFC7691B701CE100AD841F /* ASLayoutElement.h in Headers */, 698DFF471E36B7E9002891F1 /* ASLayoutSpecUtilities.h in Headers */, 9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */, - DE7EF4F81DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h in Headers */, CCCCCCD51EC3EF060087FE10 /* ASTextDebugOption.h in Headers */, CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */, 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */, diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3991080..1d37be067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ - Fix ASTextNode2 handling background color incorrectly. [Adlai Holler](https://github.com/Adlai-Holler) [#831](https://github.com/TextureGroup/Texture/pull/831/) - [NoCopyRendering] Improved performance & fixed image memory not being tagged in Instruments. [Adlai Holler](https://github.com/Adlai-Holler) [#833](https://github.com/TextureGroup/Texture/pull/833/) - Use `NS_RETURNS_RETAINED` macro to make our methods a tiny bit faster. [Adlai Holler](https://github.com/Adlai-Holler) [#843](https://github.com/TextureGroup/Texture/pull/843/) +- `ASDisplayNode, ASLayoutSpec, and ASLayoutElementStyle` now conform to `NSLocking`. They act as recursive locks. Useful locking macros have been added as `ASThread.h`. Subclasses / client code can lock these objects but should be careful as usual when dealing with locks. [Adlai Holler](https://github.com/Adlai-Holler) ## 2.6 - [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon) diff --git a/Source/ASButtonNode.mm b/Source/ASButtonNode.mm index 41e411caf..7f50dd91a 100644 --- a/Source/ASButtonNode.mm +++ b/Source/ASButtonNode.mm @@ -18,7 +18,7 @@ #import #import #import -#import +#import #import #import #import @@ -161,7 +161,7 @@ - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously - (void)updateImage { - __instanceLock__.lock(); + [self lock]; UIImage *newImage; if (self.enabled == NO && _disabledImage) { @@ -178,18 +178,18 @@ - (void)updateImage if ((_imageNode != nil || newImage != nil) && newImage != self.imageNode.image) { _imageNode.image = newImage; - __instanceLock__.unlock(); + [self unlock]; [self setNeedsLayout]; return; } - __instanceLock__.unlock(); + [self unlock]; } - (void)updateTitle { - __instanceLock__.lock(); + [self lock]; NSAttributedString *newTitle; if (self.enabled == NO && _disabledAttributedTitle) { @@ -207,19 +207,19 @@ - (void)updateTitle // Calling self.titleNode is essential here because _titleNode is lazily created by the getter. if ((_titleNode != nil || newTitle.length > 0) && [self.titleNode.attributedText isEqualToAttributedString:newTitle] == NO) { _titleNode.attributedText = newTitle; - __instanceLock__.unlock(); + [self unlock]; self.accessibilityLabel = _titleNode.accessibilityLabel; [self setNeedsLayout]; return; } - __instanceLock__.unlock(); + [self unlock]; } - (void)updateBackgroundImage { - __instanceLock__.lock(); + [self lock]; UIImage *newImage; if (self.enabled == NO && _disabledBackgroundImage) { @@ -236,25 +236,25 @@ - (void)updateBackgroundImage if ((_backgroundImageNode != nil || newImage != nil) && newImage != self.backgroundImageNode.image) { _backgroundImageNode.image = newImage; - __instanceLock__.unlock(); + [self unlock]; [self setNeedsLayout]; return; } - __instanceLock__.unlock(); + [self unlock]; } - (CGFloat)contentSpacing { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _contentSpacing; } - (void)setContentSpacing:(CGFloat)contentSpacing { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (contentSpacing == _contentSpacing) { return; } @@ -267,14 +267,14 @@ - (void)setContentSpacing:(CGFloat)contentSpacing - (BOOL)laysOutHorizontally { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _laysOutHorizontally; } - (void)setLaysOutHorizontally:(BOOL)laysOutHorizontally { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (laysOutHorizontally == _laysOutHorizontally) { return; } @@ -287,49 +287,49 @@ - (void)setLaysOutHorizontally:(BOOL)laysOutHorizontally - (ASVerticalAlignment)contentVerticalAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _contentVerticalAlignment; } - (void)setContentVerticalAlignment:(ASVerticalAlignment)contentVerticalAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _contentVerticalAlignment = contentVerticalAlignment; } - (ASHorizontalAlignment)contentHorizontalAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _contentHorizontalAlignment; } - (void)setContentHorizontalAlignment:(ASHorizontalAlignment)contentHorizontalAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _contentHorizontalAlignment = contentHorizontalAlignment; } - (UIEdgeInsets)contentEdgeInsets { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _contentEdgeInsets; } - (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _contentEdgeInsets = contentEdgeInsets; } - (ASButtonNodeImageAlignment)imageAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _imageAlignment; } - (void)setImageAlignment:(ASButtonNodeImageAlignment)imageAlignment { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _imageAlignment = imageAlignment; } @@ -349,7 +349,7 @@ - (void)setTitle:(NSString *)title withFont:(UIFont *)font withColor:(UIColor *) - (NSAttributedString *)attributedTitleForState:(UIControlState)state { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: return _normalAttributedTitle; @@ -374,7 +374,7 @@ - (NSAttributedString *)attributedTitleForState:(UIControlState)state - (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: _normalAttributedTitle = [title copy]; @@ -406,7 +406,7 @@ - (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState) - (UIImage *)imageForState:(UIControlState)state { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: return _normalImage; @@ -431,7 +431,7 @@ - (UIImage *)imageForState:(UIControlState)state - (void)setImage:(UIImage *)image forState:(UIControlState)state { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: _normalImage = image; @@ -463,7 +463,7 @@ - (void)setImage:(UIImage *)image forState:(UIControlState)state - (UIImage *)backgroundImageForState:(UIControlState)state { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: return _normalBackgroundImage; @@ -488,7 +488,7 @@ - (UIImage *)backgroundImageForState:(UIControlState)state - (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); switch (state) { case UIControlStateNormal: _normalBackgroundImage = image; @@ -525,7 +525,7 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize ASLayoutSpec *spec; ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init]; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); stack.direction = _laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical; stack.spacing = _contentSpacing; stack.horizontalAlignment = _contentHorizontalAlignment; diff --git a/Source/ASControlNode.mm b/Source/ASControlNode.mm index 702161721..697abf8d0 100644 --- a/Source/ASControlNode.mm +++ b/Source/ASControlNode.mm @@ -38,8 +38,6 @@ @interface ASControlNode () { @private - ASDN::RecursiveMutex _controlLock; - // Control Attributes BOOL _enabled; BOOL _highlighted; @@ -298,7 +296,7 @@ - (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeE // ASControlNode cannot be layer backed if adding a target ASDisplayNodeAssert(!self.isLayerBacked, @"ASControlNode is layer backed, will never be able to call target in target:action: pair."); - ASDN::MutexLocker l(_controlLock); + ASLockScopeSelf(); if (!_controlEventDispatchTable) { _controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries. @@ -352,7 +350,7 @@ - (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)con NSParameterAssert(target); NSParameterAssert(controlEvent != 0 && controlEvent != ASControlNodeEventAllEvents); - ASDN::MutexLocker l(_controlLock); + ASLockScopeSelf(); // Grab the event target action array for this event. NSMutableArray *eventTargetActionArray = _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]; @@ -374,7 +372,7 @@ - (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)con - (NSSet *)allTargets { - ASDN::MutexLocker l(_controlLock); + ASLockScopeSelf(); NSMutableSet *targets = [[NSMutableSet alloc] init]; @@ -393,7 +391,7 @@ - (void)removeTarget:(id)target action:(SEL)action forControlEvents:(ASControlNo { NSParameterAssert(controlEventMask != 0); - ASDN::MutexLocker l(_controlLock); + ASLockScopeSelf(); // Enumerate the events in the mask, removing the target-action pair for each control event included in controlEventMask. _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^ @@ -435,31 +433,31 @@ - (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent: NSMutableArray *resolvedEventTargetActionArray = [[NSMutableArray alloc] init]; - _controlLock.lock(); - - // Enumerate the events in the mask, invoking the target-action pairs for each. - _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^ - (ASControlNodeEvent controlEvent) - { - // Iterate on each target action pair - for (ASControlTargetAction *targetAction in _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]) { - ASControlTargetAction *resolvedTargetAction = [[ASControlTargetAction alloc] init]; - resolvedTargetAction.action = targetAction.action; - resolvedTargetAction.target = targetAction.target; - - // NSNull means that a nil target was set, so start at self and travel the responder chain - if (!resolvedTargetAction.target && targetAction.createdWithNoTarget) { - // if the target cannot perform the action, travel the responder chain to try to find something that does - resolvedTargetAction.target = [self.view targetForAction:resolvedTargetAction.action withSender:self]; - } - - if (resolvedTargetAction.target) { - [resolvedEventTargetActionArray addObject:resolvedTargetAction]; + { + ASLockScopeSelf(); + + // Enumerate the events in the mask, invoking the target-action pairs for each. + _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^ + (ASControlNodeEvent controlEvent) + { + // Iterate on each target action pair + for (ASControlTargetAction *targetAction in _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]) { + ASControlTargetAction *resolvedTargetAction = [[ASControlTargetAction alloc] init]; + resolvedTargetAction.action = targetAction.action; + resolvedTargetAction.target = targetAction.target; + + // NSNull means that a nil target was set, so start at self and travel the responder chain + if (!resolvedTargetAction.target && targetAction.createdWithNoTarget) { + // if the target cannot perform the action, travel the responder chain to try to find something that does + resolvedTargetAction.target = [self.view targetForAction:resolvedTargetAction.action withSender:self]; + } + + if (resolvedTargetAction.target) { + [resolvedEventTargetActionArray addObject:resolvedTargetAction]; + } } - } - }); - - _controlLock.unlock(); + }); + } //We don't want to hold the lock while calling out, we could potentially walk up the ownership tree causing a deadlock. #pragma clang diagnostic push diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm index 084f4f912..d656a8983 100644 --- a/Source/ASDisplayNode+Layout.mm +++ b/Source/ASDisplayNode+Layout.mm @@ -17,14 +17,13 @@ #import #import +#import +#import #import #import #import #import -#import - -#pragma mark - #pragma mark - ASDisplayNode (ASLayoutElement) @implementation ASDisplayNode (ASLayoutElement) diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index eca586b57..078559639 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -24,7 +24,7 @@ #import #import #import -#import +#import #import #import @@ -157,7 +157,7 @@ - (ASLayout *)layoutForYogaNode - (void)setupYogaCalculatedLayout { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); YGNodeRef yogaNode = self.style.yogaNode; uint32_t childCount = YGNodeGetChildCount(yogaNode); @@ -267,7 +267,7 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize return; } - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Prepare all children for the layout pass with the current Yoga tree configuration. ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index 49f2d917c..851155dea 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -127,7 +127,7 @@ extern NSInteger const ASDefaultDrawingPriority; * */ -@interface ASDisplayNode : NSObject +@interface ASDisplayNode : NSObject /** @name Initializing a node object */ diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 60eea7fb2..98f3f1b80 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -18,7 +18,6 @@ #import #import -#import #import #import #import @@ -35,6 +34,9 @@ #import #import #import +#import +#import +#import #import #import #import @@ -93,7 +95,7 @@ BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLaye _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node) { - ASDN::MutexLocker l(node->__instanceLock__); + ASLockScope(node); _ASPendingState *result = node->_pendingViewState; if (result == nil) { result = [[_ASPendingState alloc] init]; @@ -359,6 +361,16 @@ - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBl return self; } +- (void)lock +{ + __instanceLock__.lock(); +} + +- (void)unlock +{ + __instanceLock__.unlock(); +} + - (void)setViewBlock:(ASDisplayNodeViewBlock)viewBlock { ASDisplayNodeAssertFalse(self.nodeLoaded); diff --git a/Source/ASImageNode+AnimatedImage.mm b/Source/ASImageNode+AnimatedImage.mm index a9ef4d33f..cca62928c 100644 --- a/Source/ASImageNode+AnimatedImage.mm +++ b/Source/ASImageNode+AnimatedImage.mm @@ -20,7 +20,6 @@ #import #import #import -#import #import #import #import @@ -28,6 +27,7 @@ #import #import #import +#import #import #define ASAnimatedImageDebug 0 @@ -44,7 +44,7 @@ @implementation ASImageNode (AnimatedImage) - (void)setAnimatedImage:(id )animatedImage { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setAnimatedImage:animatedImage]; } @@ -95,13 +95,13 @@ - (void)animatedImageSet:(id )newAnimatedImage previous - (id )animatedImage { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _animatedImage; } - (void)setAnimatedImagePaused:(BOOL)animatedImagePaused { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _animatedImagePaused = animatedImagePaused; @@ -110,14 +110,14 @@ - (void)setAnimatedImagePaused:(BOOL)animatedImagePaused - (BOOL)animatedImagePaused { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _animatedImagePaused; } - (void)setCoverImageCompleted:(UIImage *)coverImage { if (ASInterfaceStateIncludesDisplay(self.interfaceState)) { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setCoverImageCompleted:coverImage]; } } @@ -135,7 +135,7 @@ - (void)_locked_setCoverImageCompleted:(UIImage *)coverImage - (void)setCoverImage:(UIImage *)coverImage { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setCoverImage:coverImage]; } @@ -176,7 +176,7 @@ - (void)setAnimatedImageRunLoopMode:(NSString *)runLoopMode - (void)setShouldAnimate:(BOOL)shouldAnimate { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setShouldAnimate:shouldAnimate]; } @@ -209,7 +209,7 @@ - (void)startAnimating { ASDisplayNodeAssertMainThread(); - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_startAnimating]; } @@ -251,7 +251,7 @@ - (void)stopAnimating { ASDisplayNodeAssertMainThread(); - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_stopAnimating]; } diff --git a/Source/ASImageNode.mm b/Source/ASImageNode.mm index b1a561eea..b0266ebc3 100644 --- a/Source/ASImageNode.mm +++ b/Source/ASImageNode.mm @@ -22,7 +22,8 @@ #import #import #import -#import +#import +#import #import #import #import @@ -305,7 +306,7 @@ - (void)setPlaceholderColor:(UIColor *)placeholderColor - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASImageNodeDrawParameters *drawParameters = [[ASImageNodeDrawParameters alloc] init]; drawParameters->_image = [self _locked_Image]; @@ -325,7 +326,7 @@ - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer // Hack for now to retain the weak entry that was created while this drawing happened drawParameters->_didDrawBlock = ^(ASWeakMapEntry *entry){ - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _weakCacheEntry = entry; }; diff --git a/Source/ASMapNode.mm b/Source/ASMapNode.mm index e3e23df15..70a8951de 100644 --- a/Source/ASMapNode.mm +++ b/Source/ASMapNode.mm @@ -22,12 +22,13 @@ #import -#import +#import #import #import #import #import #import +#import @interface ASMapNode() { @@ -107,14 +108,14 @@ - (void)didExitPreloadState - (BOOL)isLiveMap { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _liveMap; } - (void)setLiveMap:(BOOL)liveMap { ASDisplayNodeAssert(!self.isLayerBacked, @"ASMapNode can not use the interactive map feature whilst .isLayerBacked = YES, set .layerBacked = NO to use the interactive map feature."); - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (liveMap == _liveMap) { return; } @@ -126,19 +127,19 @@ - (void)setLiveMap:(BOOL)liveMap - (BOOL)needsMapReloadOnBoundsChange { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _needsMapReloadOnBoundsChange; } - (void)setNeedsMapReloadOnBoundsChange:(BOOL)needsMapReloadOnBoundsChange { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _needsMapReloadOnBoundsChange = needsMapReloadOnBoundsChange; } - (MKMapSnapshotOptions *)options { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (!_options) { _options = [[MKMapSnapshotOptions alloc] init]; _options.region = MKCoordinateRegionForMapRect(MKMapRectWorld); @@ -152,7 +153,7 @@ - (MKMapSnapshotOptions *)options - (void)setOptions:(MKMapSnapshotOptions *)options { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (!_options || ![options isEqual:_options]) { _options = options; if (self.isLiveMap) { @@ -324,7 +325,7 @@ - (void)removeLiveMap - (NSArray *)annotations { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _annotations; } @@ -332,7 +333,7 @@ - (void)setAnnotations:(NSArray *)annotations { annotations = [annotations copy] ? : @[]; - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _annotations = annotations; ASMapNodeShowAnnotationsOptions showAnnotationsOptions = self.showAnnotationsOptions; if (self.isLiveMap) { @@ -353,7 +354,7 @@ - (void)setAnnotations:(NSArray *)annotations } } --(MKCoordinateRegion)regionToFitAnnotations:(NSArray> *)annotations +- (MKCoordinateRegion)regionToFitAnnotations:(NSArray> *)annotations { if([annotations count] == 0) return MKCoordinateRegionForMapRect(MKMapRectWorld); @@ -377,12 +378,11 @@ -(MKCoordinateRegion)regionToFitAnnotations:(NSArray> *)annotat } -(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions { - ASDN::MutexLocker l(__instanceLock__); - return _showAnnotationsOptions; + return ASLockedSelf(_showAnnotationsOptions); } -(void)setShowAnnotationsOptions:(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _showAnnotationsOptions = showAnnotationsOptions; } diff --git a/Source/ASMultiplexImageNode.mm b/Source/ASMultiplexImageNode.mm index 9edf93b6a..50057dd52 100644 --- a/Source/ASMultiplexImageNode.mm +++ b/Source/ASMultiplexImageNode.mm @@ -22,12 +22,14 @@ #endif #import -#import #import +#import +#import #import #import #import #import +#import #if AS_PIN_REMOTE_IMAGE #import @@ -357,23 +359,21 @@ - (void)setDataSource:(id )dataSource - (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages { - __instanceLock__.lock(); + [self lock]; if (shouldRenderProgressImages == _shouldRenderProgressImages) { - __instanceLock__.unlock(); + [self unlock]; return; } _shouldRenderProgressImages = shouldRenderProgressImages; - - __instanceLock__.unlock(); + [self unlock]; [self _updateProgressImageBlockOnDownloaderIfNeeded]; } - (BOOL)shouldRenderProgressImages { - ASDN::MutexLocker l(__instanceLock__); - return _shouldRenderProgressImages; + return ASLockedSelf(_shouldRenderProgressImages); } #pragma mark - diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index f7c430a46..6f61da7b9 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -20,7 +20,8 @@ #import #import #import -#import +#import +#import #import #import #import @@ -29,13 +30,15 @@ #import #import +#import + #if AS_PIN_REMOTE_IMAGE #import #endif @interface ASNetworkImageNode () { - // Only access any of these with __instanceLock__. + // Only access any of these while locked. __weak id _delegate; NSURL *_URL; @@ -126,7 +129,7 @@ - (void)dealloc /// Setter for public image property. It has the side effect of setting an internal _imageWasSetExternally that prevents setting an image internally. Setting an image internally should happen with the _setImage: method - (void)setImage:(UIImage *)image { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setImage:image]; } @@ -152,7 +155,7 @@ - (void)_locked_setImage:(UIImage *)image /// Setter for private image property. See @c _locked_setImage why this is needed - (void)_setImage:(UIImage *)image { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked__setImage:image]; } @@ -181,7 +184,7 @@ - (void)setURL:(NSURL *)URL - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (ASObjectIsEqual(URL, _URL)) { return; @@ -210,13 +213,12 @@ - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset - (NSURL *)URL { - ASDN::MutexLocker l(__instanceLock__); - return _URL; + return ASLockedSelf(_URL); } - (void)setDefaultImage:(UIImage *)defaultImage { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_setDefaultImage:defaultImage]; } @@ -237,20 +239,18 @@ - (void)_locked_setDefaultImage:(UIImage *)defaultImage - (UIImage *)defaultImage { - ASDN::MutexLocker l(__instanceLock__); - return _defaultImage; + return ASLockedSelf(_defaultImage); } - (void)setCurrentImageQuality:(CGFloat)currentImageQuality { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _currentImageQuality = currentImageQuality; } - (CGFloat)currentImageQuality { - ASDN::MutexLocker l(__instanceLock__); - return _currentImageQuality; + return ASLockedSelf(_currentImageQuality); } /** @@ -262,29 +262,25 @@ - (CGFloat)currentImageQuality - (void)_setCurrentImageQuality:(CGFloat)imageQuality { dispatch_async(dispatch_get_main_queue(), ^{ - // As the setting of the image quality is dispatched the lock is gone by the time the block is executing. - // Therefore we have to grab the lock again - __instanceLock__.lock(); - _currentImageQuality = imageQuality; - __instanceLock__.unlock(); + self.currentImageQuality = imageQuality; }); } - (void)setRenderedImageQuality:(CGFloat)renderedImageQuality { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _renderedImageQuality = renderedImageQuality; } - (CGFloat)renderedImageQuality { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _renderedImageQuality; } - (void)setDelegate:(id)delegate { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _delegate = delegate; _delegateFlags.delegateDidStartFetchingData = [delegate respondsToSelector:@selector(imageNodeDidStartFetchingData:)]; @@ -296,14 +292,14 @@ - (void)setDelegate:(id)delegate - (id)delegate { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _delegate; } - (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (shouldRenderProgressImages == _shouldRenderProgressImages) { return; } @@ -315,13 +311,13 @@ - (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages - (BOOL)shouldRenderProgressImages { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shouldRenderProgressImages; } - (BOOL)placeholderShouldPersist { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return (self.image == nil && self.animatedImage == nil && _URL != nil); } @@ -332,7 +328,7 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously [super displayWillStartAsynchronously:asynchronously]; if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); NSURL *url = _URL; if (_imageLoaded == NO && url && _downloadIdentifier == nil) { @@ -344,11 +340,11 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously // Call out to the delegate. if (_delegateFlags.delegateDidLoadImageWithInfo) { - ASDN::MutexUnlocker l(__instanceLock__); + ASUnlockScope(self); auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:url sourceType:ASNetworkImageSourceSynchronousCache downloadIdentifier:nil userInfo:nil]; [_delegate imageNode:self didLoadImage:result info:info]; } else if (_delegateFlags.delegateDidLoadImage) { - ASDN::MutexUnlocker l(__instanceLock__); + ASUnlockScope(self); [_delegate imageNode:self didLoadImage:result]; } } @@ -359,9 +355,7 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously [self didEnterPreloadState]; if (self.image == nil && _downloaderFlags.downloaderImplementsSetPriority) { - __instanceLock__.lock(); - id downloadIdentifier = _downloadIdentifier; - __instanceLock__.unlock(); + id downloadIdentifier = ASLockedSelf(_downloadIdentifier); if (downloadIdentifier != nil) { [_downloader setPriority:ASImageDownloaderPriorityImminent withDownloadIdentifier:downloadIdentifier]; } @@ -374,12 +368,10 @@ - (void)didEnterVisibleState { [super didEnterVisibleState]; - __instanceLock__.lock(); - id downloadIdentifier = nil; - if (_downloaderFlags.downloaderImplementsSetPriority) { - downloadIdentifier = _downloadIdentifier; - } - __instanceLock__.unlock(); + id downloadIdentifier = ({ + ASLockScopeSelf(); + _downloaderFlags.downloaderImplementsSetPriority ? _downloadIdentifier : nil; + }); if (downloadIdentifier != nil) { [_downloader setPriority:ASImageDownloaderPriorityVisible withDownloadIdentifier:downloadIdentifier]; @@ -392,12 +384,10 @@ - (void)didExitVisibleState { [super didExitVisibleState]; - __instanceLock__.lock(); - id downloadIdentifier = nil; - if (_downloaderFlags.downloaderImplementsSetPriority) { - downloadIdentifier = _downloadIdentifier; - } - __instanceLock__.unlock(); + id downloadIdentifier = ({ + ASLockScopeSelf(); + _downloaderFlags.downloaderImplementsSetPriority ? _downloadIdentifier : nil; + }); if (downloadIdentifier != nil) { [_downloader setPriority:ASImageDownloaderPriorityPreload withDownloadIdentifier:downloadIdentifier]; @@ -410,11 +400,8 @@ - (void)didExitPreloadState { [super didExitPreloadState]; - __instanceLock__.lock(); - BOOL imageWasSetExternally = _imageWasSetExternally; - __instanceLock__.unlock(); // If the image was set explicitly we don't want to remove it while exiting the preload state - if (imageWasSetExternally) { + if (ASLockedSelf(_imageWasSetExternally)) { return; } @@ -433,7 +420,7 @@ - (void)didEnterPreloadState - (void)handleProgressImage:(UIImage *)progressImage progress:(CGFloat)progress downloadIdentifier:(nullable id)downloadIdentifier { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Getting a result back for a different download identifier, download must not have been successfully canceled if (ASObjectIsEqual(_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { @@ -453,12 +440,12 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded } // Read state. - __instanceLock__.lock(); + [self lock]; BOOL shouldRender = _shouldRenderProgressImages && ASInterfaceStateIncludesVisible(_interfaceState); id oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock; id newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil; BOOL clearAndReattempt = NO; - __instanceLock__.unlock(); + [self unlock]; // If we're already bound to the correct download, we're done. if (ASObjectIsEqual(oldDownloadIDForProgressBlock, newDownloadIDForProgressBlock)) { @@ -482,7 +469,7 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded // Update state local state with lock held. { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Check if the oldDownloadIDForProgressBlock still is the same as the _downloadIdentifierForProgressBlock if (_downloadIdentifierForProgressBlock == oldDownloadIDForProgressBlock) { _downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock; @@ -505,7 +492,7 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded - (void)_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_cancelDownloadAndClearImageWithResumePossibility:storeResume]; } @@ -529,7 +516,7 @@ - (void)_locked_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResu - (void)_cancelImageDownloadWithResumePossibility:(BOOL)storeResume { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self _locked_cancelImageDownloadWithResumePossibility:storeResume]; } @@ -564,7 +551,7 @@ - (void)_downloadImageWithCompletion:(void (^)(id ima // We need to reobtain the lock after and ensure that the task we've kicked off still matches our URL. If not, we need to cancel // it and try again. { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); url = _URL; } @@ -580,7 +567,7 @@ - (void)_downloadImageWithCompletion:(void (^)(id ima as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url); { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (ASObjectIsEqual(_URL, url)) { // The download we kicked off is correct, no need to do any more work. _downloadIdentifier = downloadIdentifier; @@ -606,13 +593,13 @@ - (void)_downloadImageWithCompletion:(void (^)(id ima - (void)_lazilyLoadImageIfNecessary { - __instanceLock__.lock(); + [self lock]; __weak id delegate = _delegate; BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData; BOOL isImageLoaded = _imageLoaded; NSURL *URL = _URL; id currentDownloadIdentifier = _downloadIdentifier; - __instanceLock__.unlock(); + [self unlock]; if (!isImageLoaded && URL != nil && currentDownloadIdentifier == nil) { if (delegateDidStartFetchingData) { @@ -621,7 +608,7 @@ - (void)_lazilyLoadImageIfNecessary if (URL.isFileURL) { dispatch_async(dispatch_get_main_queue(), ^{ - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Bail out if not the same URL anymore if (!ASObjectIsEqual(URL, _URL)) { @@ -668,11 +655,11 @@ - (void)_lazilyLoadImageIfNecessary [self _setCurrentImageQuality:1.0]; if (_delegateFlags.delegateDidLoadImageWithInfo) { - ASDN::MutexUnlocker u(__instanceLock__); + ASUnlockScope(self); auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil]; [delegate imageNode:self didLoadImage:self.image info:info]; } else if (_delegateFlags.delegateDidLoadImage) { - ASDN::MutexUnlocker u(__instanceLock__); + ASUnlockScope(self); [delegate imageNode:self didLoadImage:self.image]; } }); @@ -688,7 +675,7 @@ - (void)_lazilyLoadImageIfNecessary as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); // Grab the lock for the rest of the block - ASDN::MutexLocker l(strongSelf->__instanceLock__); + ASLockScope(strongSelf); //Getting a result back for a different download identifier, download must not have been successfully canceled if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { @@ -752,17 +739,13 @@ - (void)_lazilyLoadImageIfNecessary // lock in here if (_cache != nil) { NSUUID *cacheUUID = [NSUUID UUID]; - __instanceLock__.lock(); - _cacheUUID = cacheUUID; - __instanceLock__.unlock(); + ASLockedSelf(_cacheUUID = cacheUUID); as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL); ASImageCacherCompletion completion = ^(id imageContainer) { // If the cache UUID changed, that means this request was cancelled. - __instanceLock__.lock(); - NSUUID *currentCacheUUID = _cacheUUID; - __instanceLock__.unlock(); + auto currentCacheUUID = ASLockedSelf(_cacheUUID); if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) { return; @@ -797,7 +780,8 @@ - (void)displayDidFinish id delegate = nil; - __instanceLock__.lock(); + { + ASLockScopeSelf(); if (_delegateFlags.delegateDidFinishDecoding && self.layer.contents != nil) { /* We store the image quality in _currentImageQuality whenever _image is set. On the following displayDidFinish, we'll know that _currentImageQuality is the quality of the image that has just finished rendering. In order for this to be accurate, we @@ -812,8 +796,7 @@ - (void)displayDidFinish // Assign the delegate to be used delegate = _delegate; } - - __instanceLock__.unlock(); + } if (delegate != nil) { [delegate imageNodeDidFinishDecoding:self]; diff --git a/Source/ASScrollNode.mm b/Source/ASScrollNode.mm index 3ff008809..a7458f19d 100644 --- a/Source/ASScrollNode.mm +++ b/Source/ASScrollNode.mm @@ -18,9 +18,10 @@ #import #import #import -#import +#import #import #import +#import @interface ASScrollView : UIScrollView @end @@ -81,7 +82,7 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize { - ASDN::MutexLocker l(__instanceLock__); // Lock for using our instance variables. + ASLockScopeSelf(); // Lock for using our instance variables. ASSizeRange contentConstrainedSize = constrainedSize; if (ASScrollDirectionContainsVerticalDirection(_scrollableDirections)) { @@ -123,7 +124,7 @@ - (void)layout { [super layout]; - ASDN::MutexLocker l(__instanceLock__); // Lock for using our two instance variables. + ASLockScopeSelf(); // Lock for using our two instance variables. if (_automaticallyManagesContentSize) { CGSize contentSize = _contentCalculatedSizeFromLayout; @@ -137,13 +138,13 @@ - (void)layout - (BOOL)automaticallyManagesContentSize { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _automaticallyManagesContentSize; } - (void)setAutomaticallyManagesContentSize:(BOOL)automaticallyManagesContentSize { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _automaticallyManagesContentSize = automaticallyManagesContentSize; if (_automaticallyManagesContentSize == YES && ASScrollDirectionContainsVerticalDirection(_scrollableDirections) == NO @@ -156,13 +157,13 @@ - (void)setAutomaticallyManagesContentSize:(BOOL)automaticallyManagesContentSize - (ASScrollDirection)scrollableDirections { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _scrollableDirections; } - (void)setScrollableDirections:(ASScrollDirection)scrollableDirections { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_scrollableDirections != scrollableDirections) { _scrollableDirections = scrollableDirections; [self setNeedsLayout]; diff --git a/Source/ASTextNode.mm b/Source/ASTextNode.mm index 0b6421e7a..b57d7ccea 100644 --- a/Source/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -21,11 +21,12 @@ #if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE #import -#include +#import #import #import -#import +#import +#import #import #import #import @@ -40,6 +41,7 @@ #import #import #import +#import /** * If set, we will record all values set to attributedText into an array @@ -323,13 +325,13 @@ - (BOOL)supportsLayerBacking - (ASTextKitRenderer *)_renderer { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [self _locked_renderer]; } - (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [self _locked_rendererWithBounds:bounds]; } @@ -365,27 +367,19 @@ - (ASTextKitAttributes)_locked_rendererAttributes - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset { - __instanceLock__.lock(); - BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(textContainerInset, _textContainerInset); - if (needsUpdate) { - _textContainerInset = textContainerInset; - } - __instanceLock__.unlock(); - - if (needsUpdate) { + if (ASLockedSelfCompareAssignCustom(_textContainerInset, textContainerInset, UIEdgeInsetsEqualToEdgeInsets)) { [self setNeedsLayout]; } } - (UIEdgeInsets)textContainerInset { - ASDN::MutexLocker l(__instanceLock__); - return _textContainerInset; + return ASLockedSelf(_textContainerInset); } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); @@ -434,7 +428,7 @@ + (CGFloat)ascenderWithAttributedString:(NSAttributedString *)attributedString - (NSAttributedString *)attributedText { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _attributedText; } @@ -447,7 +441,7 @@ - (void)setAttributedText:(NSAttributedString *)attributedText // Don't hold textLock for too long. { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (ASObjectIsEqual(attributedText, _attributedText)) { return; } @@ -483,32 +477,22 @@ - (void)setAttributedText:(NSAttributedString *)attributedText - (void)setExclusionPaths:(NSArray *)exclusionPaths { - { - ASDN::MutexLocker l(__instanceLock__); - - if (ASObjectIsEqual(exclusionPaths, _exclusionPaths)) { - return; - } - - _exclusionPaths = [exclusionPaths copy]; + if (ASLockedSelfCompareAssignCopy(_exclusionPaths, exclusionPaths)) { + [self setNeedsLayout]; + [self setNeedsDisplay]; } - - [self setNeedsLayout]; - [self setNeedsDisplay]; } - (NSArray *)exclusionPaths { - ASDN::MutexLocker l(__instanceLock__); - - return _exclusionPaths; + return ASLockedSelf(_exclusionPaths); } #pragma mark - Drawing - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [[ASTextNodeDrawParameter alloc] initWithRendererAttributes:[self _locked_rendererAttributes] backgroundColor:self.backgroundColor @@ -560,7 +544,7 @@ - (id)_linkAttributeValueAtPoint:(CGPoint)point { ASDisplayNodeAssertMainThread(); - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASTextKitRenderer *renderer = [self _locked_renderer]; NSRange visibleRange = renderer.firstVisibleRange; @@ -687,14 +671,14 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer - (ASTextNodeHighlightStyle)highlightStyle { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _highlightStyle; } - (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _highlightStyle = highlightStyle; } @@ -778,7 +762,7 @@ - (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *) } if (highlightTargetLayer != nil) { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASTextKitRenderer *renderer = [self _locked_renderer]; NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock]; @@ -866,7 +850,7 @@ - (NSArray *)highlightRectsForTextRange:(NSRange)textRange - (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); NSArray *rects = [[self _locked_renderer] rectsForTextRange:textRange measureOption:measureOption]; NSMutableArray *adjustedRects = [NSMutableArray array]; @@ -884,7 +868,7 @@ - (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRende - (CGRect)trailingRect { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); CGRect rect = [[self _locked_renderer] trailingRect]; return ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding); @@ -892,7 +876,7 @@ - (CGRect)trailingRect - (CGRect)frameForTextRange:(NSRange)textRange { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); CGRect frame = [[self _locked_renderer] frameForTextRange:textRange]; return ASTextNodeAdjustRenderRectForShadowPadding(frame, self.shadowPadding); @@ -902,12 +886,9 @@ - (CGRect)frameForTextRange:(NSRange)textRange - (void)setPlaceholderColor:(UIColor *)placeholderColor { - ASDN::MutexLocker l(__instanceLock__); - - _placeholderColor = placeholderColor; - - // prevent placeholders if we don't have a color - self.placeholderEnabled = placeholderColor != nil; + if (ASLockedSelfCompareAssignCopy(_placeholderColor, placeholderColor)) { + self.placeholderEnabled = CGColorGetAlpha(placeholderColor.CGColor) > 0; + } } - (UIImage *)placeholderImage @@ -919,7 +900,7 @@ - (UIImage *)placeholderImage return nil; } - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASGraphicsBeginImageContextWithOptions(size, NO, 1.0); [self.placeholderColor setFill]; @@ -1000,7 +981,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event if (inAdditionalTruncationMessage) { NSRange visibleRange = NSMakeRange(0, 0); { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); visibleRange = [self _locked_renderer].firstVisibleRange; } NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; @@ -1079,14 +1060,14 @@ - (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer - (BOOL)_pendingLinkTap { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil; } - (BOOL)_pendingTruncationTap { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName]; } @@ -1095,96 +1076,65 @@ - (BOOL)_pendingTruncationTap - (CGColorRef)shadowColor { - ASDN::MutexLocker l(__instanceLock__); - - return _shadowColor; + return ASLockedSelf(_shadowColor); } - (void)setShadowColor:(CGColorRef)shadowColor { - __instanceLock__.lock(); + [self lock]; if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { CGColorRelease(_shadowColor); _shadowColor = CGColorRetain(shadowColor); _cachedShadowUIColor = [UIColor colorWithCGColor:shadowColor]; - __instanceLock__.unlock(); + [self unlock]; [self setNeedsDisplay]; return; } - __instanceLock__.unlock(); + [self unlock]; } - (CGSize)shadowOffset { - ASDN::MutexLocker l(__instanceLock__); - - return _shadowOffset; + return ASLockedSelf(_shadowOffset); } - (void)setShadowOffset:(CGSize)shadowOffset { - { - ASDN::MutexLocker l(__instanceLock__); - - if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) { - return; - } - _shadowOffset = shadowOffset; + if (ASLockedSelfCompareAssignCustom(_shadowOffset, shadowOffset, CGSizeEqualToSize)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (CGFloat)shadowOpacity { - ASDN::MutexLocker l(__instanceLock__); - - return _shadowOpacity; + return ASLockedSelf(_shadowOpacity); } - (void)setShadowOpacity:(CGFloat)shadowOpacity { - { - ASDN::MutexLocker l(__instanceLock__); - - if (_shadowOpacity == shadowOpacity) { - return; - } - - _shadowOpacity = shadowOpacity; + if (ASLockedSelfCompareAssign(_shadowOpacity, shadowOpacity)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (CGFloat)shadowRadius { - ASDN::MutexLocker l(__instanceLock__); - - return _shadowRadius; + return ASLockedSelf(_shadowRadius); } - (void)setShadowRadius:(CGFloat)shadowRadius { - { - ASDN::MutexLocker l(__instanceLock__); - - if (_shadowRadius == shadowRadius) { - return; - } - - _shadowRadius = shadowRadius; + if (ASLockedSelfCompareAssign(_shadowRadius, shadowRadius)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (UIEdgeInsets)shadowPadding { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [self _locked_renderer].shadower.shadowPadding; } @@ -1202,96 +1152,52 @@ - (UIEdgeInsets)shadowPadding - (NSAttributedString *)truncationAttributedText { - ASDN::MutexLocker l(__instanceLock__); - return _truncationAttributedText; + return ASLockedSelf(_truncationAttributedText); } - (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText { - { - ASDN::MutexLocker l(__instanceLock__); - - if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) { - return; - } - - _truncationAttributedText = [truncationAttributedText copy]; + if (ASLockedSelfCompareAssignCopy(_truncationAttributedText, truncationAttributedText)) { + [self _invalidateTruncationText]; } - - [self _invalidateTruncationText]; } - (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage { - { - ASDN::MutexLocker l(__instanceLock__); - - if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) { - return; - } - - _additionalTruncationMessage = [additionalTruncationMessage copy]; + if (ASLockedSelfCompareAssignCopy(_additionalTruncationMessage, additionalTruncationMessage)) { + [self _invalidateTruncationText]; } - - [self _invalidateTruncationText]; } - (void)setTruncationMode:(NSLineBreakMode)truncationMode { - { - ASDN::MutexLocker l(__instanceLock__); - - if (_truncationMode == truncationMode) { - return; - } - - _truncationMode = truncationMode; + if (ASLockedSelfCompareAssign(_truncationMode, truncationMode)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (BOOL)isTruncated { - ASDN::MutexLocker l(__instanceLock__); - - return [[self _locked_renderer] isTruncated]; + return ASLockedSelf([[self _locked_renderer] isTruncated]); } - (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors { - { - ASDN::MutexLocker l(__instanceLock__); - if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors]) { - return; - } - - _pointSizeScaleFactors = pointSizeScaleFactors; + if (ASLockedSelfCompareAssignCopy(_pointSizeScaleFactors, pointSizeScaleFactors)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines { - { - ASDN::MutexLocker l(__instanceLock__); - - if (_maximumNumberOfLines == maximumNumberOfLines) { - return; - } - - _maximumNumberOfLines = maximumNumberOfLines; + if (ASLockedSelfCompareAssign(_maximumNumberOfLines, maximumNumberOfLines)) { + [self setNeedsDisplay]; } - - [self setNeedsDisplay]; } - (NSUInteger)lineCount { - ASDN::MutexLocker l(__instanceLock__); - - return [[self _locked_renderer] lineCount]; + return ASLockedSelf([[self _locked_renderer] lineCount]); } #pragma mark - Truncation Message @@ -1299,7 +1205,7 @@ - (NSUInteger)lineCount - (void)_invalidateTruncationText { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _composedTruncationText = nil; } @@ -1312,7 +1218,7 @@ - (void)_invalidateTruncationText */ - (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Check if we even have an additional truncation message. if (!_additionalTruncationMessage) { diff --git a/Source/ASTextNode2.mm b/Source/ASTextNode2.mm index cced8e577..0569a8988 100644 --- a/Source/ASTextNode2.mm +++ b/Source/ASTextNode2.mm @@ -17,7 +17,8 @@ #import #import -#import +#import +#import #import #import @@ -30,6 +31,7 @@ #import #import #import +#import @interface ASTextCacheValue : NSObject { @package @@ -259,7 +261,7 @@ + (CGFloat)ascenderWithAttributedString:(NSAttributedString *)attributedString - (NSAttributedString *)attributedText { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _attributedText; } @@ -272,7 +274,7 @@ - (void)setAttributedText:(NSAttributedString *)attributedText // Don't hold textLock for too long. { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (ASObjectIsEqual(attributedText, _attributedText)) { return; } @@ -321,7 +323,7 @@ - (NSArray *)exclusionPaths - (void)prepareAttributedString:(NSMutableAttributedString *)attributedString { - ASDN::MutexLocker lock(__instanceLock__); + ASLockScopeSelf(); // Apply paragraph style if needed [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) { @@ -503,7 +505,7 @@ - (id)_linkAttributeValueAtPoint:(CGPoint)point inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut forHighlighting:(BOOL)highlighting { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // TODO: The copy and application of size shouldn't be required, but it is currently. // See discussion in https://github.com/TextureGroup/Texture/pull/396 @@ -589,14 +591,14 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer - (ASTextNodeHighlightStyle)highlightStyle { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _highlightStyle; } - (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _highlightStyle = highlightStyle; } @@ -679,7 +681,7 @@ - (CGRect)frameForTextRange:(NSRange)textRange - (void)setPlaceholderColor:(UIColor *)placeholderColor { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _placeholderColor = placeholderColor; @@ -749,7 +751,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event if (inAdditionalTruncationMessage) { NSRange visibleRange = NSMakeRange(0, 0); { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // TODO: The copy and application of size shouldn't be required, but it is currently. // See discussion in https://github.com/TextureGroup/Texture/pull/396 ASTextContainer *containerCopy = [_textContainer copy]; @@ -835,14 +837,14 @@ - (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer - (BOOL)_pendingLinkTap { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil; } - (BOOL)_pendingTruncationTap { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName]; } @@ -857,30 +859,30 @@ - (BOOL)_pendingTruncationTap */ - (CGColorRef)shadowColor { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shadowColor; } - (void)setShadowColor:(CGColorRef)shadowColor { - __instanceLock__.lock(); + [self lock]; if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { CGColorRelease(_shadowColor); _shadowColor = CGColorRetain(shadowColor); - __instanceLock__.unlock(); + [self unlock]; [self setNeedsDisplay]; return; } - __instanceLock__.unlock(); + [self unlock]; } - (CGSize)shadowOffset { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shadowOffset; } @@ -888,7 +890,7 @@ - (CGSize)shadowOffset - (void)setShadowOffset:(CGSize)shadowOffset { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) { return; @@ -901,7 +903,7 @@ - (void)setShadowOffset:(CGSize)shadowOffset - (CGFloat)shadowOpacity { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shadowOpacity; } @@ -909,7 +911,7 @@ - (CGFloat)shadowOpacity - (void)setShadowOpacity:(CGFloat)shadowOpacity { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_shadowOpacity == shadowOpacity) { return; @@ -923,7 +925,7 @@ - (void)setShadowOpacity:(CGFloat)shadowOpacity - (CGFloat)shadowRadius { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shadowRadius; } @@ -931,7 +933,7 @@ - (CGFloat)shadowRadius - (void)setShadowRadius:(CGFloat)shadowRadius { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_shadowRadius == shadowRadius) { return; @@ -953,7 +955,7 @@ - (void)setPointSizeScaleFactors:(NSArray *)scaleFactors { AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (ASObjectIsEqual(scaleFactors, _pointSizeScaleFactors)) { return; } @@ -966,7 +968,7 @@ - (void)setPointSizeScaleFactors:(NSArray *)scaleFactors - (NSArray *)pointSizeScaleFactors { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _pointSizeScaleFactors; } @@ -985,67 +987,47 @@ - (NSArray *)pointSizeScaleFactors - (void)_ensureTruncationText { if (_textContainer.truncationToken == nil) { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _textContainer.truncationToken = [self _locked_composedTruncationText]; } } - (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText { - { - ASDN::MutexLocker l(__instanceLock__); - - if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) { - return; - } - - _truncationAttributedText = [truncationAttributedText copy]; + if (ASLockedSelfCompareAssignCopy(_truncationAttributedText, truncationAttributedText)) { + [self _invalidateTruncationText]; } - - [self _invalidateTruncationText]; } - (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage { - { - ASDN::MutexLocker l(__instanceLock__); - - if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) { - return; - } - - _additionalTruncationMessage = [additionalTruncationMessage copy]; + if (ASLockedSelfCompareAssignCopy(_additionalTruncationMessage, additionalTruncationMessage)) { + [self _invalidateTruncationText]; } - - [self _invalidateTruncationText]; } - (void)setTruncationMode:(NSLineBreakMode)truncationMode { - ASDN::MutexLocker lock(__instanceLock__); - if (_truncationMode == truncationMode) { - return; - } - _truncationMode = truncationMode; - - ASTextTruncationType truncationType; - switch (truncationMode) { - case NSLineBreakByTruncatingHead: - truncationType = ASTextTruncationTypeStart; - break; - case NSLineBreakByTruncatingTail: - truncationType = ASTextTruncationTypeEnd; - break; - case NSLineBreakByTruncatingMiddle: - truncationType = ASTextTruncationTypeMiddle; - break; - default: - truncationType = ASTextTruncationTypeNone; + if (ASLockedSelfCompareAssign(_truncationMode, truncationMode)) { + ASTextTruncationType truncationType; + switch (truncationMode) { + case NSLineBreakByTruncatingHead: + truncationType = ASTextTruncationTypeStart; + break; + case NSLineBreakByTruncatingTail: + truncationType = ASTextTruncationTypeEnd; + break; + case NSLineBreakByTruncatingMiddle: + truncationType = ASTextTruncationTypeMiddle; + break; + default: + truncationType = ASTextTruncationTypeNone; + } + + _textContainer.truncationType = truncationType; + + [self setNeedsDisplay]; } - - _textContainer.truncationType = truncationType; - - [self setNeedsDisplay]; } - (BOOL)isTruncated @@ -1071,7 +1053,7 @@ - (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines - (NSUInteger)lineCount { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); return 0; } @@ -1090,7 +1072,7 @@ - (void)_invalidateTruncationText */ - (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); // Check if we even have an additional truncation message. if (!_additionalTruncationMessage) { diff --git a/Source/ASVideoNode.mm b/Source/ASVideoNode.mm index 9274d6fa5..85650e885 100644 --- a/Source/ASVideoNode.mm +++ b/Source/ASVideoNode.mm @@ -16,11 +16,13 @@ // #import -#import +#import +#import #import #import #import #import +#import static BOOL ASAssetIsEqual(AVAsset *asset1, AVAsset *asset2) { return ASObjectIsEqual(asset1, asset2) @@ -130,7 +132,7 @@ - (ASDisplayNode *)constructPlayerNode - (AVPlayerItem *)constructPlayerItem { ASDisplayNodeAssertMainThread(); - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); AVPlayerItem *playerItem = nil; if (_assetURL != nil) { @@ -257,9 +259,7 @@ - (void)layout - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { - __instanceLock__.lock(); - ASDisplayNode *playerNode = _playerNode; - __instanceLock__.unlock(); + ASDisplayNode *playerNode = ASLockedSelf(_playerNode); CGSize calculatedSize = constrainedSize; @@ -320,9 +320,7 @@ - (void)imageAtTime:(CMTime)imageTime completionHandler:(void(^)(UIImage *image) - (void)setVideoPlaceholderImage:(UIImage *)image { - __instanceLock__.lock(); - NSString *gravity = _gravity; - __instanceLock__.unlock(); + NSString *gravity = self.gravity; if (image != nil) { self.contentMode = ASContentModeFromVideoGravity(gravity); @@ -332,7 +330,7 @@ - (void)setVideoPlaceholderImage:(UIImage *)image - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (object == _currentPlayerItem) { if ([keyPath isEqualToString:kStatus]) { @@ -400,7 +398,7 @@ - (void)didEnterPreloadState { [super didEnterPreloadState]; - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); AVAsset *asset = self.asset; // Return immediately if the asset is nil; if (asset == nil || self.playerState != ASVideoNodePlayerStateUnknown) { @@ -441,7 +439,7 @@ - (void)didExitPreloadState [super didExitPreloadState]; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); self.player = nil; self.currentItem = nil; @@ -453,15 +451,16 @@ - (void)didEnterVisibleState { [super didEnterVisibleState]; - __instanceLock__.lock(); BOOL shouldPlay = NO; - if (_shouldBePlaying || _shouldAutoplay) { - if (_player != nil && CMTIME_IS_VALID(_lastPlaybackTime)) { - [_player seekToTime:_lastPlaybackTime]; + { + ASLockScopeSelf(); + if (_shouldBePlaying || _shouldAutoplay) { + if (_player != nil && CMTIME_IS_VALID(_lastPlaybackTime)) { + [_player seekToTime:_lastPlaybackTime]; + } + shouldPlay = YES; } - shouldPlay = YES; } - __instanceLock__.unlock(); if (shouldPlay) { [self play]; @@ -472,7 +471,7 @@ - (void)didExitVisibleState { [super didExitVisibleState]; - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_shouldBePlaying) { [self pause]; @@ -487,7 +486,7 @@ - (void)didExitVisibleState - (void)setPlayerState:(ASVideoNodePlayerState)playerState { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); ASVideoNodePlayerState oldState = _playerState; @@ -513,7 +512,7 @@ - (void)setAssetURL:(NSURL *)assetURL - (NSURL *)assetURL { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_assetURL != nil) { return _assetURL; @@ -535,7 +534,7 @@ - (void)setAsset:(AVAsset *)asset - (AVAsset *)asset { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _asset; } @@ -546,7 +545,7 @@ - (void)setAndFetchAsset:(AVAsset *)asset url:(NSURL *)assetURL [self didExitPreloadState]; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); self.videoPlaceholderImage = nil; _asset = asset; _assetURL = assetURL; @@ -557,7 +556,7 @@ - (void)setAndFetchAsset:(AVAsset *)asset url:(NSURL *)assetURL - (void)setVideoComposition:(AVVideoComposition *)videoComposition { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _videoComposition = videoComposition; _currentPlayerItem.videoComposition = videoComposition; @@ -565,13 +564,13 @@ - (void)setVideoComposition:(AVVideoComposition *)videoComposition - (AVVideoComposition *)videoComposition { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _videoComposition; } - (void)setAudioMix:(AVAudioMix *)audioMix { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _audioMix = audioMix; _currentPlayerItem.audioMix = audioMix; @@ -579,19 +578,19 @@ - (void)setAudioMix:(AVAudioMix *)audioMix - (AVAudioMix *)audioMix { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _audioMix; } - (AVPlayer *)player { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _player; } - (AVPlayerLayer *)playerLayer { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return (AVPlayerLayer *)_playerNode.layer; } @@ -618,7 +617,7 @@ - (void)setDelegate:(id)delegate - (void)setGravity:(NSString *)gravity { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_playerNode.isNodeLoaded) { ((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity; } @@ -628,19 +627,19 @@ - (void)setGravity:(NSString *)gravity - (NSString *)gravity { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _gravity; } - (BOOL)muted { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _muted; } - (void)setMuted:(BOOL)muted { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _player.muted = muted; _muted = muted; @@ -650,33 +649,31 @@ - (void)setMuted:(BOOL)muted - (void)play { - __instanceLock__.lock(); + ASLockScopeSelf(); if (![self isStateChangeValid:ASVideoNodePlayerStatePlaying]) { - __instanceLock__.unlock(); return; } if (_player == nil) { - __instanceLock__.unlock(); - [self setNeedsPreload]; - __instanceLock__.lock(); + ASUnlockScope(self); + [self setNeedsPreload]; } if (_playerNode == nil) { _playerNode = [self constructPlayerNode]; - __instanceLock__.unlock(); + { + ASUnlockScope(self); [self addSubnode:_playerNode]; - __instanceLock__.lock(); - + } + [self setNeedsLayout]; } [_player play]; _shouldBePlaying = YES; - __instanceLock__.unlock(); } - (BOOL)ready @@ -686,7 +683,7 @@ - (BOOL)ready - (void)pause { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (![self isStateChangeValid:ASVideoNodePlayerStatePaused]) { return; } @@ -696,7 +693,7 @@ - (void)pause - (BOOL)isPlaying { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return (_player.rate > 0 && !_player.error); } @@ -713,7 +710,7 @@ - (BOOL)isStateChangeValid:(ASVideoNodePlayerState)state - (void)resetToPlaceholder { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_playerNode != nil) { [_playerNode removeFromSupernode]; @@ -778,13 +775,13 @@ - (void)errorWhilePlaying:(NSNotification *)notification - (AVPlayerItem *)currentItem { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _currentPlayerItem; } - (void)setCurrentItem:(AVPlayerItem *)currentItem { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self removePlayerItemObservers:_currentPlayerItem]; @@ -797,22 +794,23 @@ - (void)setCurrentItem:(AVPlayerItem *)currentItem - (ASDisplayNode *)playerNode { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _playerNode; } - (void)setPlayerNode:(ASDisplayNode *)playerNode { - __instanceLock__.lock(); - _playerNode = playerNode; - __instanceLock__.unlock(); + { + ASLockScopeSelf(); + _playerNode = playerNode; + } [self setNeedsLayout]; } - (void)setPlayer:(AVPlayer *)player { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self removePlayerObservers:_player]; @@ -827,13 +825,13 @@ - (void)setPlayer:(AVPlayer *)player - (BOOL)shouldBePlaying { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); return _shouldBePlaying; } - (void)setShouldBePlaying:(BOOL)shouldBePlaying { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); _shouldBePlaying = shouldBePlaying; } diff --git a/Source/ASVideoPlayerNode.mm b/Source/ASVideoPlayerNode.mm index e6f766b41..7859448a6 100644 --- a/Source/ASVideoPlayerNode.mm +++ b/Source/ASVideoPlayerNode.mm @@ -25,7 +25,8 @@ #import #import -#import +#import +#import static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; @@ -167,7 +168,7 @@ - (NSURL *)assetURL { NSURL *url = nil; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if ([_pendingAsset isKindOfClass:AVURLAsset.class]) { url = ((AVURLAsset *)_pendingAsset).URL; } @@ -180,7 +181,7 @@ - (void)setAsset:(AVAsset *)asset { ASDisplayNodeAssertMainThread(); - __instanceLock__.lock(); + [self lock]; // Clean out pending asset _pendingAsset = nil; @@ -188,22 +189,18 @@ - (void)setAsset:(AVAsset *)asset // Set asset based on interface state if ((ASInterfaceStateIncludesPreload(self.interfaceState))) { // Don't hold the lock while accessing the subnode - __instanceLock__.unlock(); + [self unlock]; _videoNode.asset = asset; return; } _pendingAsset = asset; - __instanceLock__.unlock(); + [self unlock]; } - (AVAsset *)asset { - __instanceLock__.lock(); - AVAsset *asset = _pendingAsset; - __instanceLock__.unlock(); - - return asset ?: _videoNode.asset; + return ASLockedSelf(_pendingAsset) ?: _videoNode.asset; } #pragma mark - ASDisplayNode @@ -212,7 +209,7 @@ - (void)didLoad { [super didLoad]; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); [self createControls]; } } @@ -223,7 +220,7 @@ - (void)didEnterPreloadState AVAsset *pendingAsset = nil; { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); pendingAsset = _pendingAsset; _pendingAsset = nil; } @@ -244,7 +241,7 @@ - (BOOL)supportsLayerBacking - (void)createControls { { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (_controlsDisabled) { return; @@ -610,7 +607,7 @@ - (void)togglePlayPause - (void)showSpinner { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (!_spinnerNode) { @@ -643,7 +640,7 @@ - (void)showSpinner - (void)removeSpinner { - ASDN::MutexLocker l(__instanceLock__); + ASLockScopeSelf(); if (!_spinnerNode) { return; diff --git a/Source/Base/ASBaseDefines.h b/Source/Base/ASBaseDefines.h index 21b85026d..6706b6da4 100755 --- a/Source/Base/ASBaseDefines.h +++ b/Source/Base/ASBaseDefines.h @@ -232,6 +232,29 @@ ((c *) ([__val class] == [c class] ? __val : nil));\ }) +// Compare two primitives, assign if different. Returns whether the assignment happened. +#define ASCompareAssign(lvalue, newValue) ({ \ + BOOL result = (lvalue != newValue); \ + if (result) { lvalue = newValue; } \ + result; \ +}) + +#define ASCompareAssignObjects(lvalue, newValue) \ + ASCompareAssignCustom(lvalue, newValue, ASObjectIsEqual) + +// e.g. ASCompareAssignCustom(_myInsets, insets, UIEdgeInsetsEqualToEdgeInsets) +#define ASCompareAssignCustom(lvalue, newValue, isequal) ({ \ + BOOL result = !(isequal(lvalue, newValue)); \ + if (result) { lvalue = newValue; } \ + result; \ +}) + +#define ASCompareAssignCopy(lvalue, newValue) ({ \ + BOOL result = !ASObjectIsEqual(lvalue, newValue); \ + if (result) { lvalue = [newValue copyWithZone:NULL]; } \ + result; \ +}) + /** * Create a new set by mapping `collection` over `work`, ignoring nil. */ diff --git a/Source/Details/ASThread.h b/Source/Details/ASThread.h index 989db9a26..6ee8a551b 100644 --- a/Source/Details/ASThread.h +++ b/Source/Details/ASThread.h @@ -15,8 +15,6 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -#pragma once - #import #import #import @@ -26,11 +24,68 @@ #import -static inline BOOL ASDisplayNodeThreadIsMain() +ASDISPLAYNODE_INLINE BOOL ASDisplayNodeThreadIsMain() { return 0 != pthread_main_np(); } +/** + * Adds the lock to the current scope. + * + * A C version of the C++ lockers. Pass in any id. + * One benefit this has over C++ lockers is that the lock is retained. We + * had bugs in the past where an object would be deallocated while someone + * had locked its instanceLock, and we'd get a crash. This macro + * retains the locked object until it can be unlocked, which is nice. + */ +#define ASLockScope(nsLocking) \ + id __lockToken __attribute__((cleanup(_ASLockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \ + [__lockToken lock]; + +/// Same as ASLockScope(1) but lock isn't retained (be careful). +#define ASLockScopeUnowned(nsLocking) \ + __unsafe_unretained id __lockToken __attribute__((cleanup(_ASLockScopeUnownedCleanup))) = nsLocking; \ + [__lockToken lock]; + +ASDISPLAYNODE_INLINE void _ASLockScopeCleanup(id __strong * const lockPtr) { + [*lockPtr unlock]; +} + +ASDISPLAYNODE_INLINE void _ASLockScopeUnownedCleanup(id __unsafe_unretained * const lockPtr) { + [*lockPtr unlock]; +} + +/** + * Same as ASLockScope(1) but it uses self, so we can skip retain/release. + */ +#define ASLockScopeSelf() ASLockScopeUnowned(self) + +/// One-liner while holding the lock. +#define ASLocked(nsLocking, expr) ({ ASLockScope(nsLocking); expr; }) + +/// Faster self-version. +#define ASLockedSelf(expr) ({ ASLockScopeSelf(); expr; }) + +#define ASLockedSelfCompareAssign(lvalue, newValue) \ + ASLockedSelf(ASCompareAssign(lvalue, newValue)) + +#define ASLockedSelfCompareAssignObjects(lvalue, newValue) \ + ASLockedSelf(ASCompareAssignObjects(lvalue, newValue)) + +#define ASLockedSelfCompareAssignCustom(lvalue, newValue, isequal) \ + ASLockedSelf(ASCompareAssignCustom(lvalue, newValue, isequal)) + +#define ASLockedSelfCompareAssignCopy(lvalue, obj) \ + ASLockedSelf(ASCompareAssignCopy(lvalue, obj)) + +#define ASUnlockScope(nsLocking) \ + id __lockToken __attribute__((cleanup(_ASUnlockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \ + [__lockToken unlock]; + +ASDISPLAYNODE_INLINE void _ASUnlockScopeCleanup(id __strong *lockPtr) { + [*lockPtr lock]; +} + #ifdef __cplusplus #define TIME_LOCKER 0 @@ -55,13 +110,10 @@ static inline BOOL ASDisplayNodeThreadIsMain() // This MUST always execute, even when assertions are disabled. Otherwise all lock operations become no-ops! // (To be explicit, do not turn this into an NSAssert, assert(), or any other kind of statement where the // evaluation of x_ can be compiled out.) -#define ASDISPLAYNODE_THREAD_ASSERT_ON_ERROR(x_) do { \ - _Pragma("clang diagnostic push"); \ - _Pragma("clang diagnostic ignored \"-Wunused-variable\""); \ - volatile int res = (x_); \ - ASDisplayNodeCAssert(res == 0, @"Expected %@ to return 0, got %d instead", @#x_, res); \ - _Pragma("clang diagnostic pop"); \ -} while (0) +#define ASDISPLAYNODE_THREAD_ASSERT_ON_ERROR(x_) ({ \ + __unused int res = (x_); \ + ASDisplayNodeCAssert(res == 0, @"Expected %s to return 0, got %d instead. Error: %s", #x_, res, strerror(res)); \ +}) /** * Assert if the current thread owns a mutex. diff --git a/Source/Layout/ASLayoutElement.h b/Source/Layout/ASLayoutElement.h index e84c54569..01af4353a 100644 --- a/Source/Layout/ASLayoutElement.h +++ b/Source/Layout/ASLayoutElement.h @@ -174,7 +174,7 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty; - (void)style:(__kindof ASLayoutElementStyle *)style propertyDidChange:(NSString *)propertyName; @end -@interface ASLayoutElementStyle : NSObject +@interface ASLayoutElementStyle : NSObject /** * @abstract Initializes the layoutElement style with a specified delegate diff --git a/Source/Layout/ASLayoutElement.mm b/Source/Layout/ASLayoutElement.mm index bf5c61373..8baba5326 100644 --- a/Source/Layout/ASLayoutElement.mm +++ b/Source/Layout/ASLayoutElement.mm @@ -176,6 +176,18 @@ - (instancetype)init return self; } +#pragma mark - NSLocking + +- (void)lock +{ + __instanceLock__.lock(); +} + +- (void)unlock +{ + __instanceLock__.unlock(); +} + #pragma mark - ASLayoutElementStyleSize - (ASLayoutElementSize)size diff --git a/Source/Layout/ASLayoutSpec.h b/Source/Layout/ASLayoutSpec.h index 6a49792de..181ea70b2 100644 --- a/Source/Layout/ASLayoutSpec.h +++ b/Source/Layout/ASLayoutSpec.h @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN /** * A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a diff --git a/Source/Layout/ASLayoutSpec.mm b/Source/Layout/ASLayoutSpec.mm index fa275d21d..ff45add44 100644 --- a/Source/Layout/ASLayoutSpec.mm +++ b/Source/Layout/ASLayoutSpec.mm @@ -259,6 +259,18 @@ - (NSString *)asciiArtName return result; } +#pragma mark - NSLocking + +- (void)lock +{ + __instanceLock__.lock(); +} + +- (void)unlock +{ + __instanceLock__.unlock(); +} + @end #pragma mark - ASWrapperLayoutSpec diff --git a/Source/Private/ASDisplayNode+AsyncDisplay.mm b/Source/Private/ASDisplayNode+AsyncDisplay.mm index 96426d479..4d307439c 100644 --- a/Source/Private/ASDisplayNode+AsyncDisplay.mm +++ b/Source/Private/ASDisplayNode+AsyncDisplay.mm @@ -20,7 +20,7 @@ #import #import #import -#import +#import #import #import #import diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index b309bdcc2..2f3e7223d 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -234,7 +234,7 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc * ASNetworkImageNode and ASMultiplexImageNode set this to YES, because they load data from a database or server, * and are expected to support a placeholder state given that display is often blocked on slow data fetching. */ -@property (nonatomic, assign) BOOL shouldBypassEnsureDisplay; +@property (atomic) BOOL shouldBypassEnsureDisplay; /** * @abstract Checks whether a node should be scheduled for display, considering its current and new interface states. diff --git a/Source/Private/ASDisplayNode+FrameworkSubclasses.h b/Source/Private/ASDisplayNode+FrameworkSubclasses.h deleted file mode 100644 index 9b083c550..000000000 --- a/Source/Private/ASDisplayNode+FrameworkSubclasses.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// ASDisplayNode+FrameworkSubclasses.h -// Texture -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional -// grant of patent rights can be found in the PATENTS file in the same directory. -// -// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, -// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -// -// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode. -// These methods must never be called or overridden by other classes. -// - -#import -#import - -// These are included because most internal subclasses need it. -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface ASDisplayNode () -{ - // Protects access to _view, _layer, _pendingViewState, _subnodes, _supernode, and other properties which are accessed from multiple threads. - @package - ASDN::RecursiveMutex __instanceLock__; -} -@end - -NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 21f091c3f..ed92a9f7e 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -21,7 +21,6 @@ #import #import #import -#import #import /** diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index ef2d460a3..5798d53e5 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -76,6 +76,8 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo @interface ASDisplayNode () <_ASTransitionContextCompletionDelegate> { @package + ASDN::RecursiveMutex __instanceLock__; + _ASPendingState *_pendingViewState; ASInterfaceState _pendingInterfaceState; UIView *_view;