Clean up ASDisplayNode internal flags.

* Reorganise and rename `_flags` for clarity and consistency.
* Remove ambiguity between `implementsDisplay` and `hasClassDisplay`.
* Delete useless `hasWillDisplayAsyncLayer` check -- make it a simple
  subclass override point, as with `didDisplayAsyncLayer:`.
* Minor comment cleanup.
This commit is contained in:
Nadine Salter 2014-12-02 16:53:02 -08:00
parent a53bc08eaf
commit a30c087616
5 changed files with 87 additions and 83 deletions

View File

@ -74,7 +74,7 @@
/**
* @abstract Returns whether the view is synchronous.
* @abstract Returns whether the node is synchronous.
*
* @return NO if the node wraps a _ASDisplayView, YES otherwise.
*/
@ -98,7 +98,7 @@
/**
* @abstract Returns whether a node's backing view or layer is loaded.
*
* @return YES if a view is loaded, or if isLayerBacked is YES and layer is not nil; NO otherwise.
* @return YES if a view is loaded, or if layerBacked is YES and layer is not nil; NO otherwise.
*/
@property (atomic, readonly, assign, getter=isNodeLoaded) BOOL nodeLoaded;

View File

@ -103,22 +103,19 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
#pragma mark - Lifecycle
// Avoid recursive loops if a subclass implements an init method that calls -initWith*Class:
- (void)_initializeInstance
{
_contentsScaleForDisplay = ASDisplayNodeScreenScale();
_displaySentinel = [[ASSentinel alloc] init];
_flags.inWindow = NO;
_flags.isInWindow = NO;
_flags.displaysAsynchronously = YES;
// As an optimization, it may be worth a caching system that performs these checks once per class in +initialize (see above).
_flags.implementsDisplay = [[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] || [self.class respondsToSelector:@selector(displayWithParameters:isCancelled:)];
_flags.hasClassDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
_flags.hasWillDisplayAsyncLayer = ([self respondsToSelector:@selector(willDisplayAsyncLayer:)] ? 1 : 0);
_flags.hasDrawParametersForAsyncLayer = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
_flags.implementsDrawRect = ([[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
_flags.implementsImageDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
_flags.implementsDrawParameters = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
}
- (id)init
@ -140,7 +137,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
[self _initializeInstance];
_viewClass = viewClass;
_flags.isSynchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]];
_flags.synchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]];
return self;
}
@ -154,8 +151,8 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
[self _initializeInstance];
_layerClass = layerClass;
_flags.isSynchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]];
_flags.isLayerBacked = YES;
_flags.synchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]];
_flags.layerBacked = YES;
return self;
}
@ -178,7 +175,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
_view = nil;
_subnodes = nil;
if (_flags.isLayerBacked)
if (_flags.layerBacked)
_layer.delegate = nil;
_layer = nil;
@ -277,8 +274,8 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
- (UIView *)view
{
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Call to -view undefined on layer-backed nodes");
if (_flags.isLayerBacked) {
ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes");
if (_flags.layerBacked) {
return nil;
}
if (!_view) {
@ -293,7 +290,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
if (!_layer) {
ASDisplayNodeAssertMainThread();
if (!_flags.isLayerBacked) {
if (!_flags.layerBacked) {
return self.view.layer;
}
[self _loadViewOrLayerIsLayerBacked:YES];
@ -304,10 +301,10 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
// Returns nil if our view is not an _ASDisplayView, but will create it if necessary.
- (_ASDisplayView *)ensureAsyncView
{
return _flags.isSynchronous ? nil:(_ASDisplayView *)self.view;
return _flags.synchronous ? nil : (_ASDisplayView *)self.view;
}
// Returns nil if the layer is not an _ASDisplayLayer; will not create the view if nil
// Returns nil if the layer is not an _ASDisplayLayer; will not create the layer if nil.
- (_ASDisplayLayer *)asyncLayer
{
ASDN::MutexLocker l(_propertyLock);
@ -317,17 +314,17 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
- (BOOL)isNodeLoaded
{
ASDN::MutexLocker l(_propertyLock);
return (_view != nil || (_flags.isLayerBacked && _layer != nil));
return (_view != nil || (_flags.layerBacked && _layer != nil));
}
- (BOOL)isSynchronous
{
return _flags.isSynchronous;
return _flags.synchronous;
}
- (void)setSynchronous:(BOOL)flag
{
_flags.isSynchronous = flag;
_flags.synchronous = flag;
}
- (void)setLayerBacked:(BOOL)isLayerBacked
@ -336,15 +333,15 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
ASDN::MutexLocker l(_propertyLock);
ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded");
if (isLayerBacked != _flags.isLayerBacked && !_view && !_layer) {
_flags.isLayerBacked = isLayerBacked;
if (isLayerBacked != _flags.layerBacked && !_view && !_layer) {
_flags.layerBacked = isLayerBacked;
}
}
- (BOOL)isLayerBacked
{
ASDN::MutexLocker l(_propertyLock);
return _flags.isLayerBacked;
return _flags.layerBacked;
}
#pragma mark -
@ -360,10 +357,10 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
// - we haven't already
// - the width is different from the last time
// - the height is different from the last time
if (!_flags.sizeCalculated || !CGSizeEqualToSize(constrainedSize, _constrainedSize)) {
if (!_flags.isMeasured || !CGSizeEqualToSize(constrainedSize, _constrainedSize)) {
_size = [self calculateSizeThatFits:constrainedSize];
_constrainedSize = constrainedSize;
_flags.sizeCalculated = YES;
_flags.isMeasured = YES;
}
ASDisplayNodeAssertTrue(_size.width >= 0.0);
@ -387,7 +384,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
ASDisplayNodeAssertThreadAffinity(self);
// Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
if (_flags.isSynchronous)
if (_flags.synchronous)
return;
ASDN::MutexLocker l(_propertyLock);
@ -467,7 +464,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
- (void)displayImmediately
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(!_flags.isSynchronous, @"this method is designed for asynchronous mode only");
ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only");
[[self asyncLayer] displayImmediately];
}
@ -631,6 +628,10 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
#pragma mark - _ASDisplayLayerDelegate
- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer
{
}
- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer
{
// Subclass hook.
@ -648,7 +649,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
[self __exitHierarchy];
}
ASDisplayNodeAssert(_flags.isLayerBacked, @"We shouldn't get called back here if there is no layer");
ASDisplayNodeAssert(_flags.layerBacked, @"We shouldn't get called back here if there is no layer");
return (id<CAAction>)[NSNull null];
}
@ -657,9 +658,9 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASDisplayNode *to)
{
if (!from || !to) return NO;
if (from->_flags.isSynchronous) return NO;
if (to->_flags.isSynchronous) return NO;
if (from->_flags.inWindow != to->_flags.inWindow) return NO;
if (from->_flags.synchronous) return NO;
if (to->_flags.synchronous) return NO;
if (from->_flags.isInWindow != to->_flags.isInWindow) return NO;
return YES;
}
@ -960,14 +961,14 @@ static NSInteger incrementIfFound(NSInteger i) {
[_supernode _removeSubnode:self];
if (ASDisplayNodeThreadIsMain()) {
if (_flags.isLayerBacked) {
if (_flags.layerBacked) {
[_layer removeFromSuperlayer];
} else {
[_view removeFromSuperview];
}
} else {
dispatch_async(dispatch_get_main_queue(), ^{
if (_flags.isLayerBacked) {
if (_flags.layerBacked) {
[_layer removeFromSuperlayer];
} else {
[_view removeFromSuperview];
@ -985,7 +986,7 @@ static NSInteger incrementIfFound(NSInteger i) {
- (void)__incrementVisibilityNotificationsDisabled
{
ASDN::MutexLocker l(_propertyLock);
const size_t maxVisibilityIncrement = (1ULL<<visibilityNotificationsDisabledBits) - 1ULL;
const size_t maxVisibilityIncrement = (1ULL<<VISIBILITY_NOTIFICATIONS_DISABLED_BITS) - 1ULL;
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled < maxVisibilityIncrement, @"Oops, too many increments of the visibility notifications API");
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement)
_flags.visibilityNotificationsDisabled++;
@ -1019,17 +1020,17 @@ static NSInteger incrementIfFound(NSInteger i) {
- (void)__enterHierarchy
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(!_flags.isInEnterHierarchy, @"Should not cause recursive __enterHierarchy");
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"Should not cause recursive __enterHierarchy");
if (!self.inWindow && !_flags.visibilityNotificationsDisabled && ![self __hasParentWithVisibilityNotificationsDisabled]) {
self.inWindow = YES;
_flags.isInEnterHierarchy = YES;
_flags.isEnteringHierarchy = YES;
if (self.shouldRasterizeDescendants) {
// Nodes that are descendants of a rasterized container do not have views or layers, and so cannot receive visibility notifications directly via orderIn/orderOut CALayer actions. Manually send visibility notifications to rasterized descendants.
[self _recursiveWillEnterHierarchy];
} else {
[self willEnterHierarchy];
}
_flags.isInEnterHierarchy = NO;
_flags.isEnteringHierarchy = NO;
CALayer *layer = self.layer;
if (!self.layer.contents) {
@ -1041,20 +1042,20 @@ static NSInteger incrementIfFound(NSInteger i) {
- (void)__exitHierarchy
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(!_flags.isInExitHierarchy, @"Should not cause recursive __exitHierarchy");
ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"Should not cause recursive __exitHierarchy");
if (self.inWindow && !_flags.visibilityNotificationsDisabled && ![self __hasParentWithVisibilityNotificationsDisabled]) {
self.inWindow = NO;
[self.asyncLayer cancelAsyncDisplay];
_flags.isInExitHierarchy = YES;
_flags.isExitingHierarchy = YES;
if (self.shouldRasterizeDescendants) {
// Nodes that are descendants of a rasterized container do not have views or layers, and so cannot receive visibility notifications directly via orderIn/orderOut CALayer actions. Manually send visibility notifications to rasterized descendants.
[self _recursiveDidExitHierarchy];
} else {
[self didExitHierarchy];
}
_flags.isInExitHierarchy = NO;
_flags.isExitingHierarchy = NO;
}
}
@ -1064,9 +1065,9 @@ static NSInteger incrementIfFound(NSInteger i) {
return;
}
_flags.isInEnterHierarchy = YES;
_flags.isEnteringHierarchy = YES;
[self willEnterHierarchy];
_flags.isInEnterHierarchy = NO;
_flags.isEnteringHierarchy = NO;
for (ASDisplayNode *subnode in self.subnodes) {
[subnode _recursiveWillEnterHierarchy];
@ -1079,9 +1080,9 @@ static NSInteger incrementIfFound(NSInteger i) {
return;
}
_flags.isInExitHierarchy = YES;
_flags.isExitingHierarchy = YES;
[self didExitHierarchy];
_flags.isInExitHierarchy = NO;
_flags.isExitingHierarchy = NO;
for (ASDisplayNode *subnode in self.subnodes) {
[subnode _recursiveDidExitHierarchy];
@ -1139,7 +1140,7 @@ static NSInteger incrementIfFound(NSInteger i) {
{
ASDisplayNodeAssertThreadAffinity(self);
// This will cause -measure: to actually compute the size instead of returning the previously cached size
_flags.sizeCalculated = NO;
_flags.isMeasured = NO;
}
- (void)didLoad
@ -1150,15 +1151,15 @@ static NSInteger incrementIfFound(NSInteger i) {
- (void)willEnterHierarchy
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(_flags.isInEnterHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
ASDisplayNodeAssert(!_flags.isInExitHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
}
- (void)didExitHierarchy
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(_flags.isInExitHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
ASDisplayNodeAssert(!_flags.isInEnterHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
[self __exitedHierarchy];
}
@ -1316,7 +1317,7 @@ static NSInteger incrementIfFound(NSInteger i) {
// for the view/layer are still valid.
ASDN::MutexLocker l(_propertyLock);
if (_flags.isLayerBacked) {
if (_flags.layerBacked) {
[_pendingViewState applyToLayer:_layer];
} else {
[_pendingViewState applyToView:_view];
@ -1403,7 +1404,7 @@ static void _recursiveSetPreventOrCancelDisplay(ASDisplayNode *node, CALayer *la
ASDisplayNodeAssertThreadAffinity(self);
// Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
if (_flags.isSynchronous)
if (_flags.synchronous)
return;
ASDN::MutexLocker l(_propertyLock);
@ -1421,7 +1422,7 @@ static void _recursiveSetPreventOrCancelDisplay(ASDisplayNode *node, CALayer *la
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
return _flags.inWindow;
return _flags.isInWindow;
}
- (void)setInWindow:(BOOL)inWindow
@ -1429,7 +1430,7 @@ static void _recursiveSetPreventOrCancelDisplay(ASDisplayNode *node, CALayer *la
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
_flags.inWindow = inWindow;
_flags.isInWindow = inWindow;
}
+ (dispatch_queue_t)asyncSizingQueue
@ -1507,7 +1508,7 @@ static void _recursiveSetPreventOrCancelDisplay(ASDisplayNode *node, CALayer *la
- (NSString *)debugDescription
{
NSString *notableTargetDesc = (_flags.isLayerBacked ? @" [layer]" : @" [view]");
NSString *notableTargetDesc = (_flags.layerBacked ? @" [layer]" : @" [view]");
if (_view && _viewClass) { // Nonstandard view is loaded
notableTargetDesc = [NSString stringWithFormat:@" [%@ : %p]", _view.class, _view];
} else if (_layer && _layerClass) { // Nonstandard layer is loaded

View File

@ -72,7 +72,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
- (NSObject *)drawParameters
{
if (_flags.hasDrawParametersForAsyncLayer) {
if (_flags.implementsDrawParameters) {
return [self drawParametersForAsyncLayer:self.asyncLayer];
}
@ -197,7 +197,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
return image;
};
} else if (_flags.hasClassDisplay) {
} else if (_flags.implementsImageDisplay) {
// Capture drawParameters from delegate on main thread
id drawParameters = [self drawParameters];
@ -213,7 +213,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
return result;
};
} else if (_flags.implementsDisplay) {
} else if (_flags.implementsDrawRect) {
CGRect bounds = self.bounds;
if (CGRectIsEmpty(bounds)) {
@ -317,9 +317,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
if (displayBlock != NULL) {
// Call willDisplay immediately in either case
if (_flags.hasWillDisplayAsyncLayer) {
[self willDisplayAsyncLayer:self.asyncLayer];
}
[self willDisplayAsyncLayer:self.asyncLayer];
if (asynchronously) {
[transaction addOperationWithBlock:displayBlock queue:[_ASDisplayLayer displayQueue] completion:completionBlock];

View File

@ -186,7 +186,7 @@
- (BOOL)isUserInteractionEnabled
{
_bridge_prologue;
if (_flags.isLayerBacked) return NO;
if (_flags.layerBacked) return NO;
return _getFromViewOnly(userInteractionEnabled);
}
@ -319,28 +319,28 @@
- (BOOL)autoresizesSubviews
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
return _getFromViewOnly(autoresizesSubviews);
}
- (void)setAutoresizesSubviews:(BOOL)flag
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
_setToViewOnly(autoresizesSubviews, flag);
}
- (UIViewAutoresizing)autoresizingMask
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
return _getFromViewOnly(autoresizingMask);
}
- (void)setAutoresizingMask:(UIViewAutoresizing)mask
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
_setToViewOnly(autoresizingMask, mask);
}
@ -379,14 +379,14 @@
- (UIColor *)tintColor
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
return _getFromViewOnly(tintColor);
}
- (void)setTintColor:(UIColor *)color
{
_bridge_prologue;
ASDisplayNodeAssert(!_flags.isLayerBacked, @"Danger: this property is undefined on layer-backed nodes.");
ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
_setToViewOnly(tintColor, color);
}

View File

@ -25,14 +25,15 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)());
@class _ASPendingState;
// Allow 2^n increments of begin disabling hierarchy notifications
#define visibilityNotificationsDisabledBits 4
#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4
#define TIME_DISPLAYNODE_OPS (DEBUG || PROFILE)
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
{
@protected
ASDN::RecursiveMutex _propertyLock; // Protects access to the _view, _pendingViewState, _subnodes, _supernode, _renderingSubnodes, and other properties which are accessed from multiple threads.
// Protects access to _view, _layer, _pendingViewState, _subnodes, _supernode, and other properties which are accessed from multiple threads.
ASDN::RecursiveMutex _propertyLock;
ASDisplayNode * __weak _supernode;
@ -55,20 +56,24 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)());
_ASPendingState *_pendingViewState;
struct {
unsigned implementsDisplay:1;
unsigned isSynchronous:1;
unsigned isLayerBacked:1;
unsigned sizeCalculated:1;
unsigned preventOrCancelDisplay:1;
// public properties
unsigned synchronous:1;
unsigned layerBacked:1;
unsigned displaysAsynchronously:1;
unsigned shouldRasterizeDescendants:1;
unsigned visibilityNotificationsDisabled:visibilityNotificationsDisabledBits;
unsigned isInEnterHierarchy:1;
unsigned isInExitHierarchy:1;
unsigned inWindow:1;
unsigned hasWillDisplayAsyncLayer:1;
unsigned hasDrawParametersForAsyncLayer:1;
unsigned hasClassDisplay:1;
unsigned preventOrCancelDisplay:1;
// whether custom drawing is enabled
unsigned implementsDrawRect:1;
unsigned implementsImageDisplay:1;
unsigned implementsDrawParameters:1;
// internal state
unsigned isMeasured:1;
unsigned isEnteringHierarchy:1;
unsigned isExitingHierarchy:1;
unsigned isInWindow:1;
unsigned visibilityNotificationsDisabled:VISIBILITY_NOTIFICATIONS_DISABLED_BITS;
} _flags;
ASDisplayNodeExtraIvars _extra;
@ -100,7 +105,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)());
// The visibility state of the node. Changed before calling willAppear, willDisappear, and didDisappear.
@property (nonatomic, readwrite, assign, getter = isInWindow) BOOL inWindow;
// Private API for helper funcitons / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this.
// Private API for helper functions / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this.
- (BOOL)__visibilityNotificationsDisabled;
- (void)__incrementVisibilityNotificationsDisabled;
- (void)__decrementVisibilityNotificationsDisabled;