From 928c440b4c8a5f7e8ff8d5a924931bfdd0b2a930 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Wed, 23 Dec 2015 16:38:33 -0800 Subject: [PATCH] Several small optimizations, especially to _ASPendingState and other hot paths. --- AsyncDisplayKit/ASControlNode.m | 5 ++- AsyncDisplayKit/ASDisplayNode.mm | 8 ++-- AsyncDisplayKit/ASPagerNode.m | 11 +++--- AsyncDisplayKit/ASTextNode.mm | 4 +- .../Private/ASDisplayNode+AsyncDisplay.mm | 6 +-- .../Private/ASDisplayNodeInternal.h | 3 ++ AsyncDisplayKit/Private/_ASPendingState.m | 39 ++++++++++++------- 7 files changed, 48 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASControlNode.m b/AsyncDisplayKit/ASControlNode.m index 8a5d89ed11..8278b9a2fc 100644 --- a/AsyncDisplayKit/ASControlNode.m +++ b/AsyncDisplayKit/ASControlNode.m @@ -76,7 +76,6 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v if (!(self = [super init])) return nil; - _controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries. _enabled = YES; // As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on. @@ -214,6 +213,10 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v // Convert nil to [NSNull null] so that it can be used as a key for NSMapTable. if (!target) target = [NSNull null]; + + if (!_controlEventDispatchTable) { + _controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries. + } // Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5fdcb4f706..5540c7c882 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1470,9 +1470,9 @@ static NSInteger incrementIfFound(NSInteger i) { } // Helper method to summarize whether or not the node run through the display process -- (BOOL)_implementsDisplay +- (BOOL)__implementsDisplay { - return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES; + return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants; } - (void)_setupPlaceholderLayer @@ -1502,7 +1502,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) // (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders). ASDisplayNode *node = [layer asyncdisplaykit_node]; - if (!layer.contents && [node _implementsDisplay]) { + if (!layer.contents && [node __implementsDisplay]) { // For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue]. // At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm. [layer displayIfNeeded]; @@ -2148,7 +2148,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, self.asyncLayer.displaySuspended = flag; - if ([self _implementsDisplay]) { + if ([self __implementsDisplay]) { if (flag) { [_supernode subnodeDisplayDidFinish:self]; } else { diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index fef60e2e2d..eead9a305b 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -20,13 +20,14 @@ - (instancetype)init { - _flowLayout = [[UICollectionViewFlowLayout alloc] init]; - _flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - _flowLayout.minimumInteritemSpacing = 0; - _flowLayout.minimumLineSpacing = 0; + UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; + flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + flowLayout.minimumInteritemSpacing = 0; + flowLayout.minimumLineSpacing = 0; - self = [super initWithCollectionViewLayout:_flowLayout]; + self = [super initWithCollectionViewLayout:flowLayout]; if (self != nil) { + _flowLayout = flowLayout; } return self; } diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 56490d18ba..3eb5c5ba59 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -97,6 +97,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation #pragma mark - NSObject +static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; + - (instancetype)init { if (self = [super init]) { @@ -120,7 +122,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation self.opaque = NO; self.backgroundColor = [UIColor clearColor]; - self.linkAttributeNames = @[ NSLinkAttributeName ]; + self.linkAttributeNames = DefaultLinkAttributeNames; // Accessibility self.isAccessibilityElement = YES; diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index 525d1b4bf8..b0cabb67f8 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -175,8 +175,6 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, - (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock rasterizing:(BOOL)rasterizing { - id nodeClass = [self class]; - asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil; ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container."); @@ -235,7 +233,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, ASDN_DELAY_FOR_DISPLAY(); - UIImage *result = [nodeClass displayWithParameters:drawParameters isCancelled:isCancelledBlock]; + UIImage *result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock]; __ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing); return result; }; @@ -265,7 +263,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); } - [nodeClass drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; + [[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; if (isCancelledBlock()) { if (!rasterizing) { diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index e16eb99092..c4c9d2ab6f 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -144,6 +144,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) // Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents - (void)__exitHierarchy; +// Helper method to summarize whether or not the node run through the display process +- (BOOL)__implementsDisplay; + // Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated. - (void)displayImmediately; diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index a9f7088341..8d2c8f7665 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -135,19 +135,24 @@ @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; + +static CGColorRef blackColorRef = NULL; +static UIColor *defaultTintColor = nil; + - (id)init { if (!(self = [super init])) return nil; - // Default UIKit color is an RGB color - static CGColorRef black; + static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ + // Default UIKit color is an RGB color CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - black = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} ); - CFRetain(black); + blackColorRef = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} ); + CFRetain(blackColorRef); CGColorSpaceRelease(colorSpace); + defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; }); // Set defaults, these come from the defaults specified in CALayer and UIView @@ -156,7 +161,7 @@ frame = CGRectZero; bounds = CGRectZero; backgroundColor = nil; - tintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; + tintColor = defaultTintColor; contents = nil; isHidden = NO; needsDisplayOnBoundsChange = NO; @@ -172,14 +177,12 @@ transform = CATransform3DIdentity; sublayerTransform = CATransform3DIdentity; userInteractionEnabled = YES; - CFRetain(black); - shadowColor = black; + shadowColor = blackColorRef; shadowOpacity = 0.0; shadowOffset = CGSizeMake(0, -3); shadowRadius = 3; borderWidth = 0; - CFRetain(black); - borderColor = black; + borderColor = blackColorRef; isAccessibilityElement = NO; accessibilityLabel = nil; accessibilityHint = nil; @@ -376,7 +379,9 @@ return; } - CGColorRelease(shadowColor); + if (shadowColor != blackColorRef) { + CGColorRelease(shadowColor); + } shadowColor = color; CGColorRetain(shadowColor); @@ -413,7 +418,9 @@ return; } - CGColorRelease(borderColor); + if (borderColor != blackColorRef) { + CGColorRelease(borderColor); + } borderColor = color; CGColorRetain(borderColor); @@ -1002,8 +1009,14 @@ - (void)dealloc { CGColorRelease(backgroundColor); - CGColorRelease(shadowColor); - CGColorRelease(borderColor); + + if (shadowColor != blackColorRef) { + CGColorRelease(shadowColor); + } + + if (borderColor != blackColorRef) { + CGColorRelease(borderColor); + } } @end