Order-of-magnitude speedup in handling of "disable visibility notifications"

Before, it was expensive to check this value, even though it was rarely set.
Now the cost is moved to setting the value, and is made very cheap to check with _hierarchyState.
This commit is contained in:
Scott Goodson
2015-12-20 12:47:42 -08:00
parent b787020310
commit 97bb05d326
4 changed files with 24 additions and 29 deletions

View File

@@ -1251,44 +1251,41 @@ static NSInteger incrementIfFound(NSInteger i) {
}
}
- (BOOL)__visibilityNotificationsDisabled
{
ASDN::MutexLocker l(_propertyLock);
return _flags.visibilityNotificationsDisabled > 0;
}
- (void)__incrementVisibilityNotificationsDisabled
{
ASDN::MutexLocker l(_propertyLock);
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)
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement) {
_flags.visibilityNotificationsDisabled++;
}
if (_flags.visibilityNotificationsDisabled == 1) {
// Must have just transitioned from 0 to 1. Notify all subnodes that we are in a disabled state.
[self enterHierarchyState:ASHierarchyStateTransitioningSupernodes];
}
}
- (void)__decrementVisibilityNotificationsDisabled
{
ASDN::MutexLocker l(_propertyLock);
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled > 0, @"Can't decrement past 0");
if (_flags.visibilityNotificationsDisabled > 0)
if (_flags.visibilityNotificationsDisabled > 0) {
_flags.visibilityNotificationsDisabled--;
}
if (_flags.visibilityNotificationsDisabled == 0) {
// Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state.
// FIXME: This system should be revisited when refactoring and consolidating the implementation of the
// addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases,
// multiple nodes in the subtree below may have a non-zero visibilityNotification count and still have
// the ASHierarchyState bit cleared (the only value checked when reading this state).
[self exitHierarchyState:ASHierarchyStateTransitioningSupernodes];
}
}
// This uses the layer hieararchy for safety. Who knows what people might do and it would be bad to have visibilty out of sync
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled
{
CALayer *layer = _layer;
do {
ASDisplayNode *node = ASLayerToDisplayNode(layer);
if (node) {
if (node->_flags.visibilityNotificationsDisabled) {
return YES;
}
}
layer = layer.superlayer;
} while (layer);
return NO;
ASDN::MutexLocker l(_propertyLock);
return (_hierarchyState & ASHierarchyStateTransitioningSupernodes);
}
- (void)__enterHierarchy

View File

@@ -7,12 +7,10 @@
*/
#import "ASDisplayNode+DebugTiming.h"
#import "ASDisplayNodeInternal.h"
@implementation ASDisplayNode (DebugTiming)
#if TIME_DISPLAYNODE_OPS
- (NSTimeInterval)debugTimeToCreateView
{
@@ -83,6 +81,4 @@
#endif
@end

View File

@@ -37,14 +37,17 @@
typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
{
/** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */
ASHierarchyStateNormal = 0,
ASHierarchyStateNormal = 0,
/** The node has a supernode with .shouldRasterizeDescendants = YES.
Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */
ASHierarchyStateRasterized = 1 << 0,
ASHierarchyStateRasterized = 1 << 0,
/** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are
ASCellNode objects or a subnode of one, and are used in ASTableView or ASCollectionView.
These nodes also recieve regular updates to the .interfaceState property with more detailed status information. */
ASHierarchyStateRangeManaged = 1 << 1,
ASHierarchyStateRangeManaged = 1 << 1,
/** Down-propogated version of _flags.visibilityNotificationsDisabled. This flag is very rarely set, but by having it
locally available to nodes, they do not have to walk up supernodes at the critical points it is checked. */
ASHierarchyStateTransitioningSupernodes = 1 << 2
};
@interface ASDisplayNode () <_ASDisplayLayerDelegate>

View File

@@ -105,7 +105,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
NSTimeInterval _debugTimeToAddSubnodeViews;
NSTimeInterval _debugTimeForDidLoad;
#endif
}
+ (void)scheduleNodeForDisplay:(ASDisplayNode *)node;