[ASDisplayNode] Use a C function to lazily create pending view state

This commit is contained in:
Adlai Holler
2016-02-21 10:03:06 -08:00
parent 29726fbe38
commit 648dc817ad
4 changed files with 39 additions and 27 deletions

View File

@@ -79,6 +79,17 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector);
}
_ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node)
{
ASDN::MutexLocker l(node->_propertyLock);
_ASPendingState *result = node->_pendingViewState;
if (result == nil) {
result = [[_ASPendingState alloc] init];
node->_pendingViewState = result;
}
return result;
}
/**
* Returns ASDisplayNodeFlags for the givern class/instance. instance MAY BE NIL.
*
@@ -257,7 +268,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_contentsScaleForDisplay = ASScreenScale();
_displaySentinel = [[ASSentinel alloc] init];
_preferredFrameSize = CGSizeZero;
_pendingViewState = [_ASPendingState new];
}
- (id)init

View File

@@ -64,25 +64,23 @@ ASDISPLAYNODE_INLINE BOOL ASDisplayNodeShouldApplyBridgedWriteToView(ASDisplayNo
#define _getFromViewOrLayer(layerProperty, viewAndPendingViewStateProperty) __loaded ? \
(_view ? _view.viewAndPendingViewStateProperty : _layer.layerProperty )\
: self.pendingViewState.viewAndPendingViewStateProperty
: ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty
#define _setToViewOrLayer(layerProperty, layerValueExpr, viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
if (shouldApply) { (_view ? _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr) : _layer.layerProperty = (layerValueExpr)); } else { _pendingViewState.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
if (shouldApply) { (_view ? _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr) : _layer.layerProperty = (layerValueExpr)); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
#define _setToViewOnly(viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
if (shouldApply) { _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } else { _pendingViewState.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
if (shouldApply) { _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
#define _getFromViewOnly(viewAndPendingViewStateProperty) __loaded ? _view.viewAndPendingViewStateProperty : self.pendingViewState.viewAndPendingViewStateProperty
#define _getFromViewOnly(viewAndPendingViewStateProperty) __loaded ? _view.viewAndPendingViewStateProperty : ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty
#define _getFromLayer(layerProperty) __loaded ? _layer.layerProperty : self.pendingViewState.layerProperty
#define _getFromLayer(layerProperty) __loaded ? _layer.layerProperty : ASDisplayNodeGetPendingState(self).layerProperty
#define _setToLayer(layerProperty, layerValueExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingViewState.layerProperty = (layerValueExpr); }
if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNodeGetPendingState(self).layerProperty = (layerValueExpr); }
#define _messageToViewOrLayer(viewAndLayerSelector) (_view ? [_view viewAndLayerSelector] : [_layer viewAndLayerSelector])
#define _messageToLayer(layerSelector) __loaded ? [_layer layerSelector] : [self.pendingViewState layerSelector]
/**
* This category implements certain frequently-used properties and methods of UIView and CALayer so that ASDisplayNode clients can just call the view/layer methods on the node,
* with minimal loss in performance. Unlike UIView and CALayer methods, these can be called from a non-main thread until the view or layer is created.
@@ -261,10 +259,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
self.bounds = bounds;
self.position = position;
} else if (nodeLoaded && !isMainThread) {
if (!_pendingViewState.hasChanges) {
[ASPendingStateController.sharedInstance registerNode:self];
_ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
if (!pendingState.hasChanges) {
[[ASPendingStateController sharedInstance] registerNode:self];
}
_pendingViewState.frame = rect;
pendingState.frame = rect;
}
}
@@ -292,11 +291,12 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
if (ASDisplayNodeThreadIsMain()) {
_messageToViewOrLayer(setNeedsDisplay);
} else {
if (!_pendingViewState.hasChanges) {
[ASPendingStateController.sharedInstance registerNode:self];
_ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
if (!pendingState.hasChanges) {
[[ASPendingStateController sharedInstance] registerNode:self];
}
[self __setNeedsDisplay];
[_pendingViewState setNeedsDisplay];
[pendingState setNeedsDisplay];
}
} else {
[self __setNeedsDisplay];
@@ -312,13 +312,14 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
[self __setNeedsLayout];
_messageToViewOrLayer(setNeedsLayout);
} else {
if (!_pendingViewState.hasChanges) {
[ASPendingStateController.sharedInstance registerNode:self];
_ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
if (!pendingState.hasChanges) {
[[ASPendingStateController sharedInstance] registerNode:self];
}
// NOTE: We will call [self __setNeedsLayout] just before we apply
// the pending state. We need to call it on main if the node is loaded
// to support implicit hierarchy management.
[_pendingViewState setNeedsLayout];
[pendingState setNeedsLayout];
}
} else {
[self __setNeedsLayout];
@@ -511,21 +512,22 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
return _view.contentMode;
}
} else {
return self.pendingViewState.contentMode;
return ASDisplayNodeGetPendingState(self).contentMode;
}
}
- (void)setContentMode:(UIViewContentMode)contentMode
{
_bridge_prologue_write;
if (__loaded) {
BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
if (shouldApply) {
if (_flags.layerBacked) {
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
} else {
_view.contentMode = contentMode;
}
} else {
self.pendingViewState.contentMode = contentMode;
ASDisplayNodeGetPendingState(self).contentMode = contentMode;
}
}

View File

@@ -23,9 +23,13 @@
@protocol _ASDisplayLayerDelegate;
@class _ASDisplayLayer;
@class _ASPendingState;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
/// Get the pending view state for the node, creating one if needed.
_ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node);
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
{
ASDisplayNodeMethodOverrideNone = 0,
@@ -36,7 +40,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4
};
@class _ASPendingState;
@class _ASDisplayNodePosition;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
@@ -139,9 +142,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
// The _ASDisplayLayer backing the node, if any.
@property (nonatomic, readonly, retain) _ASDisplayLayer *asyncLayer;
// Creates a pendingViewState if one doesn't exist. Allows setting view properties on a bg thread before there is a view.
@property (atomic, retain, readonly) _ASPendingState *pendingViewState;
// Bitmask to check which methods an object overrides.
@property (nonatomic, assign, readonly) ASDisplayNodeMethodOverrides methodOverrides;

View File

@@ -168,13 +168,13 @@ static inline void ASDispatchSyncOnOtherThread(dispatch_block_t block) {
{
ASPendingStateController *ctrl = [ASPendingStateController sharedInstance];
ASDisplayNode *node = [ASDisplayNode new];
XCTAssertFalse(node.pendingViewState.hasChanges);
XCTAssertFalse(ASDisplayNodeGetPendingState(node).hasChanges);
[node view];
XCTAssertEqual(node.alpha, 1);
node.alpha = 0;
XCTAssertEqual(node.view.alpha, 0);
XCTAssertEqual(node.alpha, 0);
XCTAssertFalse(node.pendingViewState.hasChanges);
XCTAssertFalse(ASDisplayNodeGetPendingState(node).hasChanges);
XCTAssertFalse(ctrl.test_isFlushScheduled);
}