mirror of
https://github.com/TextureGroup/Texture.git
synced 2026-04-07 19:17:39 +00:00
Eliminate +[ASDisplayNode initialize] in production, reduce work in debug
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
- Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
- Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029)
|
||||
- Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042)
|
||||
- Eliminate `+[ASDisplayNode initialize]` in production, and drastically reduce its work when debugging. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
|
||||
|
||||
## 2.7
|
||||
|
||||
+30
-64
@@ -24,6 +24,7 @@
|
||||
#import <AsyncDisplayKit/ASCellNode+Internal.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <unordered_map>
|
||||
|
||||
#import <AsyncDisplayKit/_ASAsyncTransaction.h>
|
||||
#import <AsyncDisplayKit/_ASAsyncTransactionContainer+Private.h>
|
||||
@@ -111,51 +112,37 @@ _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ASDisplayNodeFlags for the given class/instance. instance MAY BE NIL.
|
||||
* Fetch the ASDisplayNodeFlags and ASDisplayNodeMethodOverrides for the given class.
|
||||
*
|
||||
* @param c the class, required
|
||||
* @param instance the instance, which may be nil. (If so, the class is inspected instead)
|
||||
* @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads
|
||||
* +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this
|
||||
* method on each -init and passing the instance value. While this may seem like an unlikely scenario,
|
||||
* it turns our our own internal tests use a dynamic class, so it's worth capturing this edge case.
|
||||
*
|
||||
* @return ASDisplayNode flags.
|
||||
* @param c the class, required
|
||||
* @param flags a ref to a flags struct that we will initialize
|
||||
* @param overrides a ref to an overrides field that we will initialize
|
||||
*/
|
||||
static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance)
|
||||
static void GetASDisplayNodeFlags(Class c, struct ASDisplayNodeFlags &flags, ASDisplayNodeMethodOverrides &overrides)
|
||||
{
|
||||
ASDisplayNodeCAssertNotNil(c, @"class is required");
|
||||
static ASDN::StaticMutex lock;
|
||||
static std::unordered_map<Class, std::pair<struct ASDisplayNodeFlags, ASDisplayNodeMethodOverrides>> map;
|
||||
|
||||
struct ASDisplayNodeFlags flags = {0};
|
||||
ASDN::StaticMutexLocker locker(lock);
|
||||
auto it = map.find(c);
|
||||
if (it != map.cend()) {
|
||||
auto pair = it->second;
|
||||
flags = pair.first;
|
||||
overrides = pair.second;
|
||||
return;
|
||||
}
|
||||
|
||||
flags = {0};
|
||||
|
||||
flags.isInHierarchy = NO;
|
||||
flags.displaysAsynchronously = YES;
|
||||
flags.shouldAnimateSizeChanges = YES;
|
||||
flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
|
||||
flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
|
||||
if (instance) {
|
||||
flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
|
||||
} else {
|
||||
flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ASDisplayNodeMethodOverrides for the given class
|
||||
*
|
||||
* @param c the class, required.
|
||||
*
|
||||
* @return ASDisplayNodeMethodOverrides.
|
||||
*/
|
||||
static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
{
|
||||
ASDisplayNodeCAssertNotNil(c, @"class is required");
|
||||
|
||||
ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone;
|
||||
|
||||
overrides = ASDisplayNodeMethodOverrideNone;
|
||||
|
||||
// Handling touches
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesBegan;
|
||||
@@ -201,12 +188,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits;
|
||||
}
|
||||
|
||||
return overrides;
|
||||
map.insert({c, {flags, overrides}});
|
||||
}
|
||||
|
||||
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
|
||||
+ (void)initialize
|
||||
{
|
||||
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
|
||||
if (self != [ASDisplayNode class]) {
|
||||
|
||||
// Subclasses should never override these. Use unused to prevent warnings
|
||||
@@ -222,7 +209,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
// Check if subnodes where modified during the creation of the layout
|
||||
__block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) {
|
||||
NSArray *oldSubnodes = _self.subnodes;
|
||||
ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange);
|
||||
ASLayoutSpec *layoutElement = ((ASLayoutSpec *(*)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange);
|
||||
NSArray *subnodes = _self.subnodes;
|
||||
ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
|
||||
for (NSInteger i = 0; i < oldSubnodes.count; i++) {
|
||||
@@ -231,29 +218,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
return layoutElement;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
|
||||
// when each instance is constructed. These values don't change for each class, so there is significant performance benefit
|
||||
// in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here.
|
||||
// Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path
|
||||
// (recalculating for each instance) to make sure we are always correct.
|
||||
|
||||
BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:));
|
||||
BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:));
|
||||
struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil);
|
||||
ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self);
|
||||
|
||||
__unused Class initializeSelf = self;
|
||||
|
||||
IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) {
|
||||
ASDisplayNodeAssert(node.class == initializeSelf, @"Node class %@ does not have a matching _staticInitialize method; check to ensure [super initialize] is called within any custom +initialize implementations! Overridden methods will not be called unless they are also implemented by superclass %@", node.class, initializeSelf);
|
||||
node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags;
|
||||
node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides;
|
||||
});
|
||||
|
||||
class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !AS_INITIALIZE_FRAMEWORK_MANUALLY
|
||||
+ (void)load
|
||||
@@ -274,14 +240,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)_staticInitialize
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden");
|
||||
}
|
||||
|
||||
- (void)_initializeInstance
|
||||
{
|
||||
[self _staticInitialize];
|
||||
GetASDisplayNodeFlags(self.class, _flags, _methodOverrides);
|
||||
|
||||
#if ASEVENTLOG_ENABLE
|
||||
_eventLog = [[ASEventLog alloc] initWithObject:self];
|
||||
@@ -1868,6 +1829,11 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
[self displayDidFinish];
|
||||
}
|
||||
|
||||
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)displayWillStart {}
|
||||
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
|
||||
{
|
||||
|
||||
@@ -43,19 +43,6 @@
|
||||
return nil; \
|
||||
} \
|
||||
|
||||
- (NSObject *)drawParameters
|
||||
{
|
||||
__instanceLock__.lock();
|
||||
BOOL implementsDrawParameters = _flags.implementsDrawParameters;
|
||||
__instanceLock__.unlock();
|
||||
|
||||
if (implementsDrawParameters) {
|
||||
return [self drawParametersForAsyncLayer:self.asyncLayer];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
|
||||
{
|
||||
// Skip subtrees that are hidden or zero alpha.
|
||||
@@ -191,11 +178,13 @@
|
||||
CGColorRef borderColor = self.borderColor;
|
||||
CGFloat borderWidth = self.borderWidth;
|
||||
CGFloat contentsScaleForDisplay = _contentsScaleForDisplay;
|
||||
auto asyncLayer = [self _locked_asyncLayer];
|
||||
ASDisplayNodeAssertNotNil(asyncLayer, @"Expected async layer to be non-nil since we're doing asynchronous drawing.");
|
||||
|
||||
__instanceLock__.unlock();
|
||||
|
||||
// Capture drawParameters from delegate on main thread, if this node is displaying itself rather than recursively rasterizing.
|
||||
id drawParameters = (shouldBeginRasterizing == NO ? [self drawParameters] : nil);
|
||||
id drawParameters = (shouldBeginRasterizing == NO ? [self drawParametersForAsyncLayer:asyncLayer] : nil);
|
||||
|
||||
// Only the -display methods should be called if we can't size the graphics buffer to use.
|
||||
if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) {
|
||||
|
||||
@@ -121,7 +121,6 @@ AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimest
|
||||
|
||||
unsigned implementsDrawRect:1;
|
||||
unsigned implementsImageDisplay:1;
|
||||
unsigned implementsDrawParameters:1;
|
||||
|
||||
// internal state
|
||||
unsigned isEnteringHierarchy:1;
|
||||
@@ -367,6 +366,8 @@ AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimest
|
||||
|
||||
- (BOOL)_locked_insetsLayoutMarginsFromSafeArea;
|
||||
|
||||
- (_ASDisplayLayer *)_locked_asyncLayer;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -183,6 +183,11 @@ static _ASDisplayLayerTestDelegateClassModes _class_modes;
|
||||
|
||||
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
|
||||
{
|
||||
// If the mode isn't set, pretend we don't implement this method.
|
||||
if (!(_modes & _ASDisplayLayerTestDelegateModeDrawParameters)) {
|
||||
return [super drawParametersForAsyncLayer:layer];
|
||||
}
|
||||
|
||||
_drawParametersCount++;
|
||||
return self;
|
||||
}
|
||||
@@ -196,8 +201,6 @@ static _ASDisplayLayerTestDelegateClassModes _class_modes;
|
||||
{
|
||||
if (sel_isEqual(selector, @selector(didDisplayAsyncLayer:))) {
|
||||
return (_modes & _ASDisplayLayerTestDelegateModeDidDisplay);
|
||||
} else if (sel_isEqual(selector, @selector(drawParametersForAsyncLayer:))) {
|
||||
return (_modes & _ASDisplayLayerTestDelegateModeDrawParameters);
|
||||
} else if (sel_isEqual(selector, @selector(willDisplayAsyncLayer:))) {
|
||||
return (_modes & _ASDisplayLayerTestDelegateModeWillDisplay);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user