mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
[ASDisplayNode] Improve locking in ASDisplayNode (#3172)
* Improve locking in ASDisplayNode * Address first bunch of comments * Changed `view` and `layer` methods for locking * Adress comments
This commit is contained in:
committed by
GitHub
parent
b1cfd76cee
commit
e6ee24debc
@@ -118,12 +118,12 @@
|
|||||||
_viewControllerNode.frame = self.bounds;
|
_viewControllerNode.frame = self.bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_locked_rootNodeDidInvalidateSize
|
- (void)_rootNodeDidInvalidateSize
|
||||||
{
|
{
|
||||||
if (_interactionDelegate != nil) {
|
if (_interactionDelegate != nil) {
|
||||||
[_interactionDelegate nodeDidInvalidateSize:self];
|
[_interactionDelegate nodeDidInvalidateSize:self];
|
||||||
} else {
|
} else {
|
||||||
[super _locked_rootNodeDidInvalidateSize];
|
[super _rootNodeDidInvalidateSize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -237,9 +237,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Check if subnodes where modified during the creation of the layout
|
// Check if subnodes where modified during the creation of the layout
|
||||||
if (self == [ASDisplayNode class]) {
|
if (self == [ASDisplayNode class]) {
|
||||||
__block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) {
|
__block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) {
|
||||||
NSArray *oldSubnodes = _self.subnodes;
|
NSArray *oldSubnodes = _self.subnodes;
|
||||||
ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_layoutElementThatFits:), sizeRange);
|
ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange);
|
||||||
NSArray *subnodes = _self.subnodes;
|
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.");
|
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++) {
|
for (NSInteger i = 0; i < oldSubnodes.count; i++) {
|
||||||
@@ -403,7 +403,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
|
- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if ([self _isNodeLoaded]) {
|
|
||||||
|
if ([self _locked_isNodeLoaded]) {
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
ASDN::MutexUnlocker l(__instanceLock__);
|
ASDN::MutexUnlocker l(__instanceLock__);
|
||||||
body(self);
|
body(self);
|
||||||
@@ -431,7 +432,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
// reference to subnodes.
|
// reference to subnodes.
|
||||||
|
|
||||||
for (ASDisplayNode *subnode in _subnodes)
|
for (ASDisplayNode *subnode in _subnodes)
|
||||||
[subnode __setSupernode:nil];
|
[subnode _setSupernode:nil];
|
||||||
|
|
||||||
// Trampoline any UIKit ivars' deallocation to main
|
// Trampoline any UIKit ivars' deallocation to main
|
||||||
if (ASDisplayNodeThreadIsMain() == NO) {
|
if (ASDisplayNodeThreadIsMain() == NO) {
|
||||||
@@ -447,7 +448,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
|
// TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
|
||||||
[self __setSupernode:nil];
|
[self _setSupernode:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_scheduleIvarsForMainDeallocation
|
- (void)_scheduleIvarsForMainDeallocation
|
||||||
@@ -558,10 +559,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self);
|
ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self);
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (_flags.layerBacked)
|
if (_flags.layerBacked) {
|
||||||
_pendingViewState = [_ASPendingState pendingViewStateFromLayer:_layer];
|
_pendingViewState = [_ASPendingState pendingViewStateFromLayer:_layer];
|
||||||
else
|
} else {
|
||||||
_pendingViewState = [_ASPendingState pendingViewStateFromView:_view];
|
_pendingViewState = [_ASPendingState pendingViewStateFromView:_view];
|
||||||
|
}
|
||||||
|
|
||||||
[_view removeFromSuperview];
|
[_view removeFromSuperview];
|
||||||
_view = nil;
|
_view = nil;
|
||||||
@@ -571,21 +573,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
_layer = nil;
|
_layer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)__loadNode
|
- (BOOL)_locked_shouldLoadViewOrLayer
|
||||||
{
|
|
||||||
[self layer];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)__shouldLoadViewOrLayer
|
|
||||||
{
|
{
|
||||||
return !(_hierarchyState & ASHierarchyStateRasterized);
|
return !(_hierarchyState & ASHierarchyStateRasterized);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIView *)_viewToLoad
|
- (UIView *)_locked_viewToLoad
|
||||||
{
|
{
|
||||||
UIView *view;
|
UIView *view = nil;
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
if (_viewBlock) {
|
if (_viewBlock) {
|
||||||
view = _viewBlock();
|
view = _viewBlock();
|
||||||
ASDisplayNodeAssertNotNil(view, @"View block returned nil");
|
ASDisplayNodeAssertNotNil(view, @"View block returned nil");
|
||||||
@@ -617,12 +612,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CALayer *)_layerToLoad
|
- (CALayer *)_locked_layerToLoad
|
||||||
{
|
{
|
||||||
CALayer *layer;
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
ASDisplayNodeAssert(_flags.layerBacked, @"_layerToLoad is only for layer-backed nodes");
|
ASDisplayNodeAssert(_flags.layerBacked, @"_layerToLoad is only for layer-backed nodes");
|
||||||
|
|
||||||
|
CALayer *layer = nil;
|
||||||
if (_layerBlock) {
|
if (_layerBlock) {
|
||||||
layer = _layerBlock();
|
layer = _layerBlock();
|
||||||
ASDisplayNodeAssertNotNil(layer, @"Layer block returned nil");
|
ASDisplayNodeAssertNotNil(layer, @"Layer block returned nil");
|
||||||
@@ -640,6 +634,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)_loadViewOrLayerIsLayerBacked:(BOOL)isLayerBacked
|
- (void)_loadViewOrLayerIsLayerBacked:(BOOL)isLayerBacked
|
||||||
|
{
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
@@ -647,13 +642,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self __shouldLoadViewOrLayer]) {
|
if (![self _locked_shouldLoadViewOrLayer]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLayerBacked) {
|
if (isLayerBacked) {
|
||||||
TIME_SCOPED(_debugTimeToCreateView);
|
TIME_SCOPED(_debugTimeToCreateView);
|
||||||
_layer = [self _layerToLoad];
|
_layer = [self _locked_layerToLoad];
|
||||||
static int ASLayerDelegateAssociationKey;
|
static int ASLayerDelegateAssociationKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -667,13 +662,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
objc_setAssociatedObject(_layer, &ASLayerDelegateAssociationKey, instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
objc_setAssociatedObject(_layer, &ASLayerDelegateAssociationKey, instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
} else {
|
} else {
|
||||||
TIME_SCOPED(_debugTimeToCreateView);
|
TIME_SCOPED(_debugTimeToCreateView);
|
||||||
_view = [self _viewToLoad];
|
_view = [self _locked_viewToLoad];
|
||||||
_view.asyncdisplaykit_node = self;
|
_view.asyncdisplaykit_node = self;
|
||||||
_layer = _view.layer;
|
_layer = _view.layer;
|
||||||
}
|
}
|
||||||
_layer.asyncdisplaykit_node = self;
|
_layer.asyncdisplaykit_node = self;
|
||||||
|
|
||||||
self.asyncLayer.asyncDelegate = self;
|
self._locked_asyncLayer.asyncDelegate = self;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TIME_SCOPED(_debugTimeToApplyPendingState);
|
TIME_SCOPED(_debugTimeToApplyPendingState);
|
||||||
@@ -685,20 +681,24 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
TIME_SCOPED(_debugTimeForDidLoad);
|
TIME_SCOPED(_debugTimeForDidLoad);
|
||||||
[self __didLoad];
|
[self _didLoad];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)__didLoad
|
- (void)_didLoad
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
ASDisplayNodeLogEvent(self, @"didLoad");
|
ASDisplayNodeLogEvent(self, @"didLoad");
|
||||||
|
|
||||||
[self didLoad];
|
[self didLoad];
|
||||||
for (ASDisplayNodeDidLoadBlock block in _onDidLoadBlocks) {
|
|
||||||
|
__instanceLock__.lock();
|
||||||
|
NSArray *onDidLoadBlocks = [_onDidLoadBlocks copy];
|
||||||
|
_onDidLoadBlocks = nil;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
for (ASDisplayNodeDidLoadBlock block in onDidLoadBlocks) {
|
||||||
block(self);
|
block(self);
|
||||||
}
|
}
|
||||||
_onDidLoadBlocks = nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didLoad
|
- (void)didLoad
|
||||||
@@ -713,14 +713,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
if (ASDisplayNodeThreadIsMain()) {
|
if (ASDisplayNodeThreadIsMain()) {
|
||||||
// Because the view and layer can only be created and destroyed on Main, that is also the only thread
|
// Because the view and layer can only be created and destroyed on Main, that is also the only thread
|
||||||
// where the state of this property can change. As an optimization, we can avoid locking.
|
// where the state of this property can change. As an optimization, we can avoid locking.
|
||||||
return [self _isNodeLoaded];
|
return [self _locked_isNodeLoaded];
|
||||||
} else {
|
} else {
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return [self _isNodeLoaded];
|
return [self _locked_isNodeLoaded];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_isNodeLoaded
|
- (BOOL)_locked_isNodeLoaded
|
||||||
{
|
{
|
||||||
return (_view != nil || (_layer != nil && _flags.layerBacked));
|
return (_view != nil || (_layer != nil && _flags.layerBacked));
|
||||||
}
|
}
|
||||||
@@ -733,23 +733,27 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
if (_flags.layerBacked) {
|
if (_flags.layerBacked) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if (!_view) {
|
|
||||||
|
if (_view == nil) {
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
[self _loadViewOrLayerIsLayerBacked:NO];
|
[self _loadViewOrLayerIsLayerBacked:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
return _view;
|
return _view;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CALayer *)layer
|
- (CALayer *)layer
|
||||||
{
|
{
|
||||||
if (!_layer) {
|
if (_layer == nil) {
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (!_flags.layerBacked) {
|
if (!_flags.layerBacked) {
|
||||||
return self.view.layer;
|
return self.view.layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _loadViewOrLayerIsLayerBacked:YES];
|
[self _loadViewOrLayerIsLayerBacked:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
return _layer;
|
return _layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,6 +761,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
- (_ASDisplayLayer *)asyncLayer
|
- (_ASDisplayLayer *)asyncLayer
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
return [self _locked_asyncLayer];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (_ASDisplayLayer *)_locked_asyncLayer
|
||||||
|
{
|
||||||
return [_layer isKindOfClass:[_ASDisplayLayer class]] ? (_ASDisplayLayer *)_layer : nil;
|
return [_layer isKindOfClass:[_ASDisplayLayer class]] ? (_ASDisplayLayer *)_layer : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,7 +783,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
|
|
||||||
- (void)setLayerBacked:(BOOL)isLayerBacked
|
- (void)setLayerBacked:(BOOL)isLayerBacked
|
||||||
{
|
{
|
||||||
if (![self.class layerBackedNodesEnabled]) return;
|
if (![self.class layerBackedNodesEnabled]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded");
|
ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded");
|
||||||
@@ -923,8 +934,6 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
|
|
||||||
- (void)__setNeedsLayout
|
- (void)__setNeedsLayout
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
[self invalidateCalculatedLayout];
|
[self invalidateCalculatedLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1042,7 +1051,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
// In this case, we need to detect that we've already asked to be resized to match this
|
// In this case, we need to detect that we've already asked to be resized to match this
|
||||||
// particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
|
// particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
|
||||||
nextLayout->requestedLayoutFromAbove = YES;
|
nextLayout->requestedLayoutFromAbove = YES;
|
||||||
[self setNeedsLayoutFromAbove];
|
[self _setNeedsLayoutFromAbove];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to transition to nextLayout
|
// Prepare to transition to nextLayout
|
||||||
@@ -1060,7 +1069,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
|
|
||||||
- (ASSizeRange)_locked_constrainedSizeForLayoutPass
|
- (ASSizeRange)_locked_constrainedSizeForLayoutPass
|
||||||
{
|
{
|
||||||
// TODO: The logic in -setNeedsLayoutFromAbove seems correct and doesn't use this method.
|
// TODO: The logic in -_setNeedsLayoutFromAbove seems correct and doesn't use this method.
|
||||||
// logic seems correct. For what case does -this method need to do the CGSizeEqual checks?
|
// logic seems correct. For what case does -this method need to do the CGSizeEqual checks?
|
||||||
// IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK
|
// IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK
|
||||||
|
|
||||||
@@ -1137,7 +1146,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get layout element from the node
|
// Get layout element from the node
|
||||||
id<ASLayoutElement> layoutElement = [self _layoutElementThatFits:constrainedSize];
|
id<ASLayoutElement> layoutElement = [self _locked_layoutElementThatFits:constrainedSize];
|
||||||
|
|
||||||
// Certain properties are necessary to set on an element of type ASLayoutSpec
|
// Certain properties are necessary to set on an element of type ASLayoutSpec
|
||||||
if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
|
if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
|
||||||
@@ -1200,7 +1209,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero;
|
return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<ASLayoutElement>)_layoutElementThatFits:(ASSizeRange)constrainedSize
|
- (id<ASLayoutElement>)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize
|
||||||
{
|
{
|
||||||
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||||
|
|
||||||
@@ -1249,7 +1258,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
return _calculatedDisplayNodeLayout->layout;
|
return _calculatedDisplayNodeLayout->layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLayout>)displayNodeLayout
|
- (void)_setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLayout>)displayNodeLayout
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
@@ -1284,34 +1293,34 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
* @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know
|
* @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know
|
||||||
* that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen.
|
* that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen.
|
||||||
*/
|
*/
|
||||||
- (void)setNeedsLayoutFromAbove
|
- (void)_setNeedsLayoutFromAbove
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
|
|
||||||
__instanceLock__.lock();
|
|
||||||
|
|
||||||
// Mark the node for layout in the next layout pass
|
// Mark the node for layout in the next layout pass
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
|
|
||||||
|
__instanceLock__.lock();
|
||||||
// Escalate to the root; entire tree must allow adjustments so the layout fits the new child.
|
// Escalate to the root; entire tree must allow adjustments so the layout fits the new child.
|
||||||
// Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack)
|
// Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack)
|
||||||
ASDisplayNode *supernode = _supernode;
|
ASDisplayNode *supernode = _supernode;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
if (supernode) {
|
if (supernode) {
|
||||||
// Threading model requires that we unlock before calling a method on our parent.
|
// Threading model requires that we unlock before calling a method on our parent.
|
||||||
__instanceLock__.unlock();
|
[supernode _setNeedsLayoutFromAbove];
|
||||||
[supernode setNeedsLayoutFromAbove];
|
} else {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the root node method know that the size was invalidated
|
// Let the root node method know that the size was invalidated
|
||||||
[self _locked_rootNodeDidInvalidateSize];
|
[self _rootNodeDidInvalidateSize];
|
||||||
|
}
|
||||||
__instanceLock__.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_locked_rootNodeDidInvalidateSize
|
- (void)_rootNodeDidInvalidateSize
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
|
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
||||||
|
|
||||||
|
__instanceLock__.lock();
|
||||||
|
|
||||||
// We are the root node and need to re-flow the layout; at least one child needs a new size.
|
// We are the root node and need to re-flow the layout; at least one child needs a new size.
|
||||||
CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size);
|
CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size);
|
||||||
@@ -1324,19 +1333,22 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
constrainedSize = _calculatedDisplayNodeLayout->constrainedSize;
|
constrainedSize = _calculatedDisplayNodeLayout->constrainedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
// Perform a measurement pass to get the full tree layout, adapting to the child's new size.
|
// Perform a measurement pass to get the full tree layout, adapting to the child's new size.
|
||||||
ASLayout *layout = [self layoutThatFits:constrainedSize];
|
ASLayout *layout = [self layoutThatFits:constrainedSize];
|
||||||
|
|
||||||
// Check if the returned layout has a different size than our current bounds.
|
// Check if the returned layout has a different size than our current bounds.
|
||||||
if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) {
|
if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) {
|
||||||
// If so, inform our container we need an update (e.g Table, Collection, ViewController, etc).
|
// If so, inform our container we need an update (e.g Table, Collection, ViewController, etc).
|
||||||
[self _locked_displayNodeDidInvalidateSizeNewSize:layout.size];
|
[self displayNodeDidInvalidateSizeNewSize:layout.size];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)size
|
- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
|
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
||||||
|
|
||||||
// The default implementation of display node changes the size of itself to the new size
|
// The default implementation of display node changes the size of itself to the new size
|
||||||
CGRect oldBounds = self.bounds;
|
CGRect oldBounds = self.bounds;
|
||||||
@@ -1361,14 +1373,10 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
||||||
|
|
||||||
__instanceLock__.lock();
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (_calculatedDisplayNodeLayout->isDirty()) {
|
if (! _calculatedDisplayNodeLayout->isDirty()) {
|
||||||
__instanceLock__.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _locked_layoutSublayouts];
|
[self _locked_layoutSublayouts];
|
||||||
__instanceLock__.unlock();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_locked_layoutSublayouts
|
- (void)_locked_layoutSublayouts
|
||||||
@@ -1496,7 +1504,7 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
constrainedSize,
|
constrainedSize,
|
||||||
constrainedSize.max
|
constrainedSize.max
|
||||||
);
|
);
|
||||||
[self setCalculatedDisplayNodeLayout:pendingLayout];
|
[self _setCalculatedDisplayNodeLayout:pendingLayout];
|
||||||
|
|
||||||
// Apply complete layout transitions for all subnodes
|
// Apply complete layout transitions for all subnodes
|
||||||
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
|
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
|
||||||
@@ -1541,8 +1549,11 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
|
|
||||||
- (void)cancelLayoutTransition
|
- (void)cancelLayoutTransition
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
if (_transitionInProgress) {
|
BOOL transitionInProgress = _transitionInProgress;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if (transitionInProgress) {
|
||||||
// Cancel transition in progress
|
// Cancel transition in progress
|
||||||
[self _finishOrCancelTransition];
|
[self _finishOrCancelTransition];
|
||||||
|
|
||||||
@@ -1655,8 +1666,10 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
|
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
|
||||||
{
|
{
|
||||||
if ([context isAnimated] == NO) {
|
if ([context isAnimated] == NO) {
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
[self _locked_layoutSublayouts];
|
[self _locked_layoutSublayouts];
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
[context completeTransition:YES];
|
[context completeTransition:YES];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1746,7 +1759,11 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
*/
|
*/
|
||||||
- (void)didCompleteLayoutTransition:(id<ASContextTransitioning>)context
|
- (void)didCompleteLayoutTransition:(id<ASContextTransitioning>)context
|
||||||
{
|
{
|
||||||
[_pendingLayoutTransition applySubnodeRemovals];
|
__instanceLock__.lock();
|
||||||
|
ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
[pendingLayoutTransition applySubnodeRemovals];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark <_ASTransitionContextCompletionDelegate>
|
#pragma mark <_ASTransitionContextCompletionDelegate>
|
||||||
@@ -1757,7 +1774,10 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
*/
|
*/
|
||||||
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
|
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
[self didCompleteLayoutTransition:context];
|
[self didCompleteLayoutTransition:context];
|
||||||
|
|
||||||
_pendingLayoutTransitionContext = nil;
|
_pendingLayoutTransitionContext = nil;
|
||||||
|
|
||||||
[self _pendingLayoutTransitionDidComplete];
|
[self _pendingLayoutTransitionDidComplete];
|
||||||
@@ -1768,10 +1788,13 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
*/
|
*/
|
||||||
- (void)_completePendingLayoutTransition
|
- (void)_completePendingLayoutTransition
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
if (_pendingLayoutTransition) {
|
ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition;
|
||||||
[self setCalculatedDisplayNodeLayout:_pendingLayoutTransition.pendingLayout];
|
__instanceLock__.unlock();
|
||||||
[self _completeLayoutTransition:_pendingLayoutTransition];
|
|
||||||
|
if (pendingLayoutTransition != nil) {
|
||||||
|
[self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout];
|
||||||
|
[self _completeLayoutTransition:pendingLayoutTransition];
|
||||||
}
|
}
|
||||||
[self _pendingLayoutTransitionDidComplete];
|
[self _pendingLayoutTransitionDidComplete];
|
||||||
}
|
}
|
||||||
@@ -1800,15 +1823,16 @@ ASLayoutElementFinalLayoutElementDefault
|
|||||||
|
|
||||||
- (void)_pendingLayoutTransitionDidComplete
|
- (void)_pendingLayoutTransitionDidComplete
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
// Subclass hook
|
// Subclass hook
|
||||||
[self calculatedLayoutDidChange];
|
[self calculatedLayoutDidChange];
|
||||||
|
|
||||||
|
// Grab lock after calling out to subclass
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
// We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go.
|
// We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go.
|
||||||
// This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously.
|
// This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously.
|
||||||
// First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync.
|
// First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync.
|
||||||
if (_placeholderEnabled && !_placeholderImage && [self __locked_displaysAsynchronously]) {
|
if (_placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) {
|
||||||
|
|
||||||
// Zero-sized nodes do not require a placeholder.
|
// Zero-sized nodes do not require a placeholder.
|
||||||
ASLayout *layout = _calculatedDisplayNodeLayout->layout;
|
ASLayout *layout = _calculatedDisplayNodeLayout->layout;
|
||||||
@@ -1849,14 +1873,13 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
- (BOOL)displaysAsynchronously
|
- (BOOL)displaysAsynchronously
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return [self __locked_displaysAsynchronously];
|
return [self _locked_displaysAsynchronously];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core implementation of -displaysAsynchronously.
|
* Core implementation of -displaysAsynchronously.
|
||||||
* Must be called with __instanceLock__ held.
|
|
||||||
*/
|
*/
|
||||||
- (BOOL)__locked_displaysAsynchronously
|
- (BOOL)_locked_displaysAsynchronously
|
||||||
{
|
{
|
||||||
return _flags.synchronous == NO && _flags.displaysAsynchronously;
|
return _flags.synchronous == NO && _flags.displaysAsynchronously;
|
||||||
}
|
}
|
||||||
@@ -1865,18 +1888,20 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
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.synchronous)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (_flags.displaysAsynchronously == displaysAsynchronously)
|
// Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
|
||||||
|
if (_flags.synchronous) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_flags.displaysAsynchronously == displaysAsynchronously) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_flags.displaysAsynchronously = displaysAsynchronously;
|
_flags.displaysAsynchronously = displaysAsynchronously;
|
||||||
|
|
||||||
self.asyncLayer.displaysAsynchronously = displaysAsynchronously;
|
self._locked_asyncLayer.displaysAsynchronously = displaysAsynchronously;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldRasterizeDescendants
|
- (BOOL)shouldRasterizeDescendants
|
||||||
@@ -1950,8 +1975,9 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (_contentsScaleForDisplay == contentsScaleForDisplay)
|
if (_contentsScaleForDisplay == contentsScaleForDisplay) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_contentsScaleForDisplay = contentsScaleForDisplay;
|
_contentsScaleForDisplay = contentsScaleForDisplay;
|
||||||
}
|
}
|
||||||
@@ -1961,14 +1987,12 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only");
|
ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only");
|
||||||
|
|
||||||
[[self asyncLayer] displayImmediately];
|
[self.asyncLayer displayImmediately];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)recursivelyDisplayImmediately
|
- (void)recursivelyDisplayImmediately
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
for (ASDisplayNode *child in self.subnodes) {
|
||||||
|
|
||||||
for (ASDisplayNode *child in _subnodes) {
|
|
||||||
[child recursivelyDisplayImmediately];
|
[child recursivelyDisplayImmediately];
|
||||||
}
|
}
|
||||||
[self displayImmediately];
|
[self displayImmediately];
|
||||||
@@ -1976,10 +2000,18 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
|
|
||||||
- (void)__setNeedsDisplay
|
- (void)__setNeedsDisplay
|
||||||
{
|
{
|
||||||
|
BOOL shouldScheduleForDisplay = NO;
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
|
BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
|
||||||
// FIXME: This should not need to recursively display, so create a non-recursive variant.
|
// FIXME: This should not need to recursively display, so create a non-recursive variant.
|
||||||
// The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
|
// The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
|
||||||
if (_layer && !_flags.synchronous && nowDisplay && [self __implementsDisplay]) {
|
if (_layer != nil && !_flags.synchronous && nowDisplay && [self _implementsDisplay]) {
|
||||||
|
shouldScheduleForDisplay = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldScheduleForDisplay) {
|
||||||
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2005,8 +2037,10 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method to summarize whether or not the node run through the display process
|
/// Helper method to summarize whether or not the node run through the display process
|
||||||
- (BOOL)__implementsDisplay
|
- (BOOL)_implementsDisplay
|
||||||
{
|
{
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.shouldRasterizeDescendants ||
|
return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.shouldRasterizeDescendants ||
|
||||||
_flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
|
_flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
|
||||||
}
|
}
|
||||||
@@ -2017,6 +2051,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
|
// No lock needed as _pendingDisplayNodes is main thread only
|
||||||
if (!_pendingDisplayNodes) {
|
if (!_pendingDisplayNodes) {
|
||||||
_pendingDisplayNodes = [[ASWeakSet alloc] init];
|
_pendingDisplayNodes = [[ASWeakSet alloc] init];
|
||||||
}
|
}
|
||||||
@@ -2030,12 +2065,16 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
|
// No lock for _pendingDisplayNodes needed as it's main thread only
|
||||||
[_pendingDisplayNodes removeObject:node];
|
[_pendingDisplayNodes removeObject:node];
|
||||||
|
|
||||||
if (_pendingDisplayNodes.isEmpty) {
|
if (_pendingDisplayNodes.isEmpty) {
|
||||||
[self hierarchyDisplayDidFinish];
|
|
||||||
|
|
||||||
if (_placeholderLayer.superlayer && ![self placeholderShouldPersist]) {
|
[self hierarchyDisplayDidFinish];
|
||||||
|
BOOL placeholderShouldPersist = [self placeholderShouldPersist];
|
||||||
|
|
||||||
|
__instanceLock__.lock();
|
||||||
|
if (_placeholderLayer.superlayer && !placeholderShouldPersist) {
|
||||||
void (^cleanupBlock)() = ^{
|
void (^cleanupBlock)() = ^{
|
||||||
[_placeholderLayer removeFromSuperlayer];
|
[_placeholderLayer removeFromSuperlayer];
|
||||||
};
|
};
|
||||||
@@ -2050,6 +2089,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
cleanupBlock();
|
cleanupBlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
__instanceLock__.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2060,7 +2100,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
|||||||
|
|
||||||
// Helper method to determine if it's safe to call setNeedsDisplay on a layer without throwing away the content.
|
// Helper method to determine if it's safe to call setNeedsDisplay on a layer without throwing away the content.
|
||||||
// For details look at the comment on the canCallSetNeedsDisplayOfLayer flag
|
// For details look at the comment on the canCallSetNeedsDisplayOfLayer flag
|
||||||
- (BOOL)__canCallSetNeedsDisplayOfLayer
|
- (BOOL)_canCallSetNeedsDisplayOfLayer
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return _flags.canCallSetNeedsDisplayOfLayer;
|
return _flags.canCallSetNeedsDisplayOfLayer;
|
||||||
@@ -2078,14 +2118,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
|
|
||||||
ASDisplayNode *node = [layer asyncdisplaykit_node];
|
ASDisplayNode *node = [layer asyncdisplaykit_node];
|
||||||
|
|
||||||
if (node.isSynchronous && [node __canCallSetNeedsDisplayOfLayer]) {
|
if (node.isSynchronous && [node _canCallSetNeedsDisplayOfLayer]) {
|
||||||
// Layers for UIKit components that are wrapped within a node needs to be set to be displayed as the contents of
|
// Layers for UIKit components that are wrapped within a node needs to be set to be displayed as the contents of
|
||||||
// the layer get's cleared and would not be recreated otherwise.
|
// the layer get's cleared and would not be recreated otherwise.
|
||||||
// We do not call this for _ASDisplayLayer as an optimization.
|
// We do not call this for _ASDisplayLayer as an optimization.
|
||||||
[layer setNeedsDisplay];
|
[layer setNeedsDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([node __implementsDisplay]) {
|
if ([node _implementsDisplay]) {
|
||||||
// For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue].
|
// 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.
|
// At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm.
|
||||||
[layer displayIfNeeded];
|
[layer displayIfNeeded];
|
||||||
@@ -2132,21 +2172,28 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
|
|
||||||
- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
|
- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
|
||||||
{
|
{
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
_flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay;
|
_flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldBypassEnsureDisplay
|
- (BOOL)shouldBypassEnsureDisplay
|
||||||
{
|
{
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return _flags.shouldBypassEnsureDisplay;
|
return _flags.shouldBypassEnsureDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale
|
- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale
|
||||||
|
{
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (contentsScale != self.contentsScaleForDisplay) {
|
if (contentsScale == _contentsScaleForDisplay) {
|
||||||
self.contentsScaleForDisplay = contentsScale;
|
return;
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_contentsScaleForDisplay = contentsScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setNeedsDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale
|
- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale
|
||||||
@@ -2199,27 +2246,28 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
|||||||
- (void)setDisplaySuspended:(BOOL)flag
|
- (void)setDisplaySuspended:(BOOL)flag
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
|
__instanceLock__.lock();
|
||||||
|
|
||||||
// Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
|
// Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
|
||||||
if (_flags.synchronous)
|
if (_flags.synchronous || _flags.displaySuspended == flag) {
|
||||||
return;
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
if (_flags.displaySuspended == flag)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_flags.displaySuspended = flag;
|
_flags.displaySuspended = flag;
|
||||||
|
|
||||||
self.asyncLayer.displaySuspended = flag;
|
self._locked_asyncLayer.displaySuspended = flag;
|
||||||
|
|
||||||
if ([self __implementsDisplay]) {
|
ASDisplayNode *supernode = _supernode;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if ([self _implementsDisplay]) {
|
||||||
// Display start and finish methods needs to happen on the main thread
|
// Display start and finish methods needs to happen on the main thread
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
if (flag) {
|
if (flag) {
|
||||||
[_supernode subnodeDisplayDidFinish:self];
|
[supernode subnodeDisplayDidFinish:self];
|
||||||
} else {
|
} else {
|
||||||
[_supernode subnodeDisplayWillStart:self];
|
[supernode subnodeDisplayWillStart:self];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2232,6 +2280,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (drawingPriority == ASDefaultDrawingPriority) {
|
if (drawingPriority == ASDefaultDrawingPriority) {
|
||||||
_flags.hasCustomDrawingPriority = NO;
|
_flags.hasCustomDrawingPriority = NO;
|
||||||
objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, nil, OBJC_ASSOCIATION_ASSIGN);
|
objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, nil, OBJC_ASSOCIATION_ASSIGN);
|
||||||
@@ -2245,11 +2294,13 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (!_flags.hasCustomDrawingPriority)
|
|
||||||
|
if (!_flags.hasCustomDrawingPriority) {
|
||||||
return ASDefaultDrawingPriority;
|
return ASDefaultDrawingPriority;
|
||||||
else
|
} else {
|
||||||
return [objc_getAssociatedObject(self, ASDisplayNodeDrawingPriorityKey) integerValue];
|
return [objc_getAssociatedObject(self, ASDisplayNodeDrawingPriorityKey) integerValue];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark <_ASDisplayLayerDelegate>
|
#pragma mark <_ASDisplayLayerDelegate>
|
||||||
@@ -2277,7 +2328,11 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
|||||||
// in case current node takes longer to display than it's subnodes, treat it as a dependent node
|
// in case current node takes longer to display than it's subnodes, treat it as a dependent node
|
||||||
[self _pendingNodeWillDisplay:self];
|
[self _pendingNodeWillDisplay:self];
|
||||||
|
|
||||||
[_supernode subnodeDisplayWillStart:self];
|
__instanceLock__.lock();
|
||||||
|
ASDisplayNode *supernode = _supernode;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
[supernode subnodeDisplayWillStart:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)displayDidFinish
|
- (void)displayDidFinish
|
||||||
@@ -2287,16 +2342,22 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
|||||||
ASDisplayNodeLogEvent(self, @"displayDidFinish");
|
ASDisplayNodeLogEvent(self, @"displayDidFinish");
|
||||||
[self _pendingNodeDidDisplay:self];
|
[self _pendingNodeDidDisplay:self];
|
||||||
|
|
||||||
[_supernode subnodeDisplayDidFinish:self];
|
__instanceLock__.lock();
|
||||||
|
ASDisplayNode *supernode = _supernode;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
[supernode subnodeDisplayDidFinish:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode
|
- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode
|
||||||
{
|
{
|
||||||
|
// Subclass hook
|
||||||
[self _pendingNodeWillDisplay:subnode];
|
[self _pendingNodeWillDisplay:subnode];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode
|
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode
|
||||||
{
|
{
|
||||||
|
// Subclass hook
|
||||||
[self _pendingNodeDidDisplay:subnode];
|
[self _pendingNodeDidDisplay:subnode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2509,7 +2570,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
return _supernode;
|
return _supernode;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)__setSupernode:(ASDisplayNode *)newSupernode
|
- (void)_setSupernode:(ASDisplayNode *)newSupernode
|
||||||
{
|
{
|
||||||
BOOL supernodeDidChange = NO;
|
BOOL supernodeDidChange = NO;
|
||||||
ASDisplayNode *oldSupernode = nil;
|
ASDisplayNode *oldSupernode = nil;
|
||||||
@@ -2571,7 +2632,11 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
// Otherwise we will exit the hierarchy when our view/layer does so
|
// Otherwise we will exit the hierarchy when our view/layer does so
|
||||||
// which has some nice carry-over machinery to handle cases where we are removed from a hierarchy
|
// which has some nice carry-over machinery to handle cases where we are removed from a hierarchy
|
||||||
// and then added into it again shortly after.
|
// and then added into it again shortly after.
|
||||||
if (parentWasOrIsRasterized && _flags.isInHierarchy) {
|
__instanceLock__.lock();
|
||||||
|
BOOL isInHierarchy = _flags.isInHierarchy;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if (parentWasOrIsRasterized && isInHierarchy) {
|
||||||
[self __exitHierarchy];
|
[self __exitHierarchy];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2640,7 +2705,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
// This call will apply our .hierarchyState to the new subnode.
|
// This call will apply our .hierarchyState to the new subnode.
|
||||||
// If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
|
// If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
|
||||||
[subnode __setSupernode:self];
|
[subnode _setSupernode:self];
|
||||||
|
|
||||||
// If this subnode will be rasterized, update its hierarchy state & enter hierarchy if needed
|
// If this subnode will be rasterized, update its hierarchy state & enter hierarchy if needed
|
||||||
if (nodeIsInRasterizedTree(self)) {
|
if (nodeIsInRasterizedTree(self)) {
|
||||||
@@ -2684,7 +2749,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
// Because the view and layer can only be created and destroyed on Main, that is also the only thread
|
// Because the view and layer can only be created and destroyed on Main, that is also the only thread
|
||||||
// where the view and layer can change. We can avoid locking.
|
// where the view and layer can change. We can avoid locking.
|
||||||
|
|
||||||
// If we can use view API, do. Due to an apple bug, -insertSubview:atIndex: actually wants a LAYER index, which we pass in
|
// If we can use view API, do. Due to an apple bug, -insertSubview:atIndex: actually wants a LAYER index,
|
||||||
|
// which we pass in.
|
||||||
if (canUseViewAPI(self, subnode)) {
|
if (canUseViewAPI(self, subnode)) {
|
||||||
[_view insertSubview:subnode.view atIndex:idx];
|
[_view insertSubview:subnode.view atIndex:idx];
|
||||||
} else {
|
} else {
|
||||||
@@ -2725,16 +2791,20 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
- (void)_addSubnodeViewsAndLayers
|
- (void)_addSubnodeViewsAndLayers
|
||||||
{
|
{
|
||||||
for (ASDisplayNode *node in [_subnodes copy]) {
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
|
for (ASDisplayNode *node in self.subnodes) {
|
||||||
[self _addSubnodeSubviewOrSublayer:node];
|
[self _addSubnodeSubviewOrSublayer:node];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_addSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode
|
- (void)_addSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
// Due to a bug in Apple's framework we have to use the layer index to insert a subview
|
// Due to a bug in Apple's framework we have to use the layer index to insert a subview
|
||||||
// so just use the count of the sublayers to add the subnode
|
// so just use the count of the sublayers to add the subnode
|
||||||
NSInteger idx = _layer.sublayers.count;
|
NSInteger idx = _layer.sublayers.count; // No locking is needed as it's main thread only
|
||||||
[self _insertSubnodeSubviewOrSublayer:subnode atIndex:idx];
|
[self _insertSubnodeSubviewOrSublayer:subnode atIndex:idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2971,7 +3041,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
[_subnodes removeObjectIdenticalTo:subnode];
|
[_subnodes removeObjectIdenticalTo:subnode];
|
||||||
__instanceLock__.unlock();
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
[subnode __setSupernode:nil];
|
[subnode _setSupernode:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeFromSupernode
|
- (void)removeFromSupernode
|
||||||
@@ -3023,13 +3093,16 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
- (void)__incrementVisibilityNotificationsDisabled
|
- (void)__incrementVisibilityNotificationsDisabled
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
const size_t maxVisibilityIncrement = (1ULL<<VISIBILITY_NOTIFICATIONS_DISABLED_BITS) - 1ULL;
|
const size_t maxVisibilityIncrement = (1ULL<<VISIBILITY_NOTIFICATIONS_DISABLED_BITS) - 1ULL;
|
||||||
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled < maxVisibilityIncrement, @"Oops, too many increments of the visibility notifications API");
|
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled < maxVisibilityIncrement, @"Oops, too many increments of the visibility notifications API");
|
||||||
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement) {
|
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement) {
|
||||||
_flags.visibilityNotificationsDisabled++;
|
_flags.visibilityNotificationsDisabled++;
|
||||||
}
|
}
|
||||||
if (_flags.visibilityNotificationsDisabled == 1) {
|
BOOL visibilityNotificationsDisabled = (_flags.visibilityNotificationsDisabled == 1);
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if (visibilityNotificationsDisabled) {
|
||||||
// Must have just transitioned from 0 to 1. Notify all subnodes that we are in a disabled state.
|
// Must have just transitioned from 0 to 1. Notify all subnodes that we are in a disabled state.
|
||||||
[self enterHierarchyState:ASHierarchyStateTransitioningSupernodes];
|
[self enterHierarchyState:ASHierarchyStateTransitioningSupernodes];
|
||||||
}
|
}
|
||||||
@@ -3037,12 +3110,15 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
- (void)__decrementVisibilityNotificationsDisabled
|
- (void)__decrementVisibilityNotificationsDisabled
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled > 0, @"Can't decrement past 0");
|
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled > 0, @"Can't decrement past 0");
|
||||||
if (_flags.visibilityNotificationsDisabled > 0) {
|
if (_flags.visibilityNotificationsDisabled > 0) {
|
||||||
_flags.visibilityNotificationsDisabled--;
|
_flags.visibilityNotificationsDisabled--;
|
||||||
}
|
}
|
||||||
if (_flags.visibilityNotificationsDisabled == 0) {
|
BOOL visibilityNotificationsDisabled = (_flags.visibilityNotificationsDisabled == 0);
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if (visibilityNotificationsDisabled) {
|
||||||
// Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state.
|
// 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
|
// 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,
|
// addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases,
|
||||||
@@ -3056,19 +3132,19 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
- (void)_locked_layoutPlaceholderIfNecessary
|
- (void)_locked_layoutPlaceholderIfNecessary
|
||||||
{
|
{
|
||||||
if ([self _shouldHavePlaceholderLayer]) {
|
if ([self _locked_shouldHavePlaceholderLayer]) {
|
||||||
[self _setupPlaceholderLayerIfNeeded];
|
[self _locked_setupPlaceholderLayerIfNeeded];
|
||||||
}
|
}
|
||||||
// Update the placeholderLayer size in case the node size has changed since the placeholder was added.
|
// Update the placeholderLayer size in case the node size has changed since the placeholder was added.
|
||||||
_placeholderLayer.frame = self.threadSafeBounds;
|
_placeholderLayer.frame = self.threadSafeBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_shouldHavePlaceholderLayer
|
- (BOOL)_locked_shouldHavePlaceholderLayer
|
||||||
{
|
{
|
||||||
return (_placeholderEnabled && [self __implementsDisplay]);
|
return (_placeholderEnabled && [self _implementsDisplay]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_setupPlaceholderLayerIfNeeded
|
- (void)_locked_setupPlaceholderLayerIfNeeded
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
@@ -3147,10 +3223,10 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
CALayer *layer = self.layer;
|
CALayer *layer = self.layer;
|
||||||
[layer setNeedsDisplay];
|
[layer setNeedsDisplay];
|
||||||
|
|
||||||
if ([self _shouldHavePlaceholderLayer]) {
|
if ([self _locked_shouldHavePlaceholderLayer]) {
|
||||||
[CATransaction begin];
|
[CATransaction begin];
|
||||||
[CATransaction setDisableActions:YES];
|
[CATransaction setDisableActions:YES];
|
||||||
[self _setupPlaceholderLayerIfNeeded];
|
[self _locked_setupPlaceholderLayerIfNeeded];
|
||||||
_placeholderLayer.opacity = 1.0;
|
_placeholderLayer.opacity = 1.0;
|
||||||
[CATransaction commit];
|
[CATransaction commit];
|
||||||
[layer addSublayer:_placeholderLayer];
|
[layer addSublayer:_placeholderLayer];
|
||||||
@@ -3175,7 +3251,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
_flags.isExitingHierarchy = YES;
|
_flags.isExitingHierarchy = YES;
|
||||||
_flags.isInHierarchy = NO;
|
_flags.isInHierarchy = NO;
|
||||||
|
|
||||||
[self.asyncLayer cancelAsyncDisplay];
|
[self._locked_asyncLayer cancelAsyncDisplay];
|
||||||
|
|
||||||
// Don't call -didExitHierarchy while holding __instanceLock__.
|
// Don't call -didExitHierarchy while holding __instanceLock__.
|
||||||
// This method and subsequent ones (i.e -interfaceState and didExit(.*)State)
|
// This method and subsequent ones (i.e -interfaceState and didExit(.*)State)
|
||||||
@@ -3312,36 +3388,6 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_recursiveWillEnterHierarchy
|
|
||||||
{
|
|
||||||
if (_flags.visibilityNotificationsDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_flags.isEnteringHierarchy = YES;
|
|
||||||
[self willEnterHierarchy];
|
|
||||||
_flags.isEnteringHierarchy = NO;
|
|
||||||
|
|
||||||
for (ASDisplayNode *subnode in self.subnodes) {
|
|
||||||
[subnode _recursiveWillEnterHierarchy];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_recursiveDidExitHierarchy
|
|
||||||
{
|
|
||||||
if (_flags.visibilityNotificationsDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_flags.isExitingHierarchy = YES;
|
|
||||||
[self didExitHierarchy];
|
|
||||||
_flags.isExitingHierarchy = NO;
|
|
||||||
|
|
||||||
for (ASDisplayNode *subnode in self.subnodes) {
|
|
||||||
[subnode _recursiveDidExitHierarchy];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Interface State
|
#pragma mark - Interface State
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3462,8 +3508,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
// NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
|
// NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
|
||||||
// internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
|
// internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
|
||||||
if (!ASInterfaceStateIncludesVisible(newState)) {
|
if (!ASInterfaceStateIncludesVisible(newState)) {
|
||||||
// Check __implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
|
// Check _implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
|
||||||
if ([self __implementsDisplay]) {
|
if ([self _implementsDisplay]) {
|
||||||
if (nowDisplay) {
|
if (nowDisplay) {
|
||||||
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
||||||
} else {
|
} else {
|
||||||
@@ -3671,11 +3717,14 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
// This method is only implemented on UIView on iOS 6+.
|
// This method is only implemented on UIView on iOS 6+.
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (!_view)
|
// No locking needed as it's main thread only
|
||||||
|
UIView *view = _view;
|
||||||
|
if (view == nil) {
|
||||||
return YES;
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
// If we reach the base implementation, forward up the view hierarchy.
|
// If we reach the base implementation, forward up the view hierarchy.
|
||||||
UIView *superview = _view.superview;
|
UIView *superview = view.superview;
|
||||||
return [superview gestureRecognizerShouldBegin:gestureRecognizer];
|
return [superview gestureRecognizerShouldBegin:gestureRecognizer];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3719,17 +3768,19 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
// If no view/layer properties were set before the view/layer were created, _pendingViewState will be nil and the default values
|
// If no view/layer properties were set before the view/layer were created, _pendingViewState will be nil and the default values
|
||||||
// for the view/layer are still valid.
|
// for the view/layer are still valid.
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
|
||||||
|
|
||||||
[self applyPendingViewState];
|
[self applyPendingViewState];
|
||||||
|
|
||||||
// TODO: move this into real pending state
|
__instanceLock__.lock();
|
||||||
|
|
||||||
if (_flags.displaySuspended) {
|
if (_flags.displaySuspended) {
|
||||||
self.asyncLayer.displaySuspended = YES;
|
self._locked_asyncLayer.displaySuspended = YES;
|
||||||
}
|
}
|
||||||
if (!_flags.displaysAsynchronously) {
|
if (!_flags.displaysAsynchronously) {
|
||||||
self.asyncLayer.displaysAsynchronously = NO;
|
self._locked_asyncLayer.displaysAsynchronously = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__instanceLock__.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applyPendingViewState
|
- (void)applyPendingViewState
|
||||||
@@ -3944,16 +3995,23 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
|
|
||||||
- (ASPrimitiveTraitCollection)primitiveTraitCollection
|
- (ASPrimitiveTraitCollection)primitiveTraitCollection
|
||||||
{
|
{
|
||||||
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return _primitiveTraitCollection;
|
return _primitiveTraitCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
|
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
|
||||||
{
|
{
|
||||||
|
__instanceLock__.lock();
|
||||||
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) {
|
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) {
|
||||||
_primitiveTraitCollection = traitCollection;
|
_primitiveTraitCollection = traitCollection;
|
||||||
ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
|
ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
[self asyncTraitCollectionDidChange];
|
[self asyncTraitCollectionDidChange];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__instanceLock__.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASTraitCollection *)asyncTraitCollection
|
- (ASTraitCollection *)asyncTraitCollection
|
||||||
|
|||||||
@@ -34,12 +34,16 @@
|
|||||||
|
|
||||||
- (NSObject *)drawParameters
|
- (NSObject *)drawParameters
|
||||||
{
|
{
|
||||||
if (_flags.implementsDrawParameters) {
|
__instanceLock__.lock();
|
||||||
return [self drawParametersForAsyncLayer:self.asyncLayer];
|
BOOL implementsDrawParameters = _flags.implementsDrawParameters;
|
||||||
}
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
|
if (implementsDrawParameters) {
|
||||||
|
return [self drawParametersForAsyncLayer:self.asyncLayer];
|
||||||
|
} else {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
|
- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
|
||||||
{
|
{
|
||||||
@@ -48,7 +52,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__instanceLock__.lock();
|
||||||
BOOL rasterizingFromAscendent = (_hierarchyState & ASHierarchyStateRasterized);
|
BOOL rasterizingFromAscendent = (_hierarchyState & ASHierarchyStateRasterized);
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
// if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers
|
// if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers
|
||||||
if (rasterizingFromAscendent) {
|
if (rasterizingFromAscendent) {
|
||||||
@@ -171,11 +177,11 @@
|
|||||||
CGRect bounds = self.bounds;
|
CGRect bounds = self.bounds;
|
||||||
CGFloat contentsScaleForDisplay = _contentsScaleForDisplay;
|
CGFloat contentsScaleForDisplay = _contentsScaleForDisplay;
|
||||||
|
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
// Capture drawParameters from delegate on main thread, if this node is displaying itself rather than recursively rasterizing.
|
// 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 drawParameters] : nil);
|
||||||
|
|
||||||
__instanceLock__.unlock();
|
|
||||||
|
|
||||||
// Only the -display methods should be called if we can't size the graphics buffer to use.
|
// Only the -display methods should be called if we can't size the graphics buffer to use.
|
||||||
if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) {
|
if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) {
|
||||||
return nil;
|
return nil;
|
||||||
@@ -225,10 +231,20 @@
|
|||||||
CGContextRef currentContext = UIGraphicsGetCurrentContext();
|
CGContextRef currentContext = UIGraphicsGetCurrentContext();
|
||||||
UIImage *image = nil;
|
UIImage *image = nil;
|
||||||
|
|
||||||
|
ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = nil;
|
||||||
|
ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = nil;
|
||||||
|
if (currentContext) {
|
||||||
|
__instanceLock__.lock();
|
||||||
|
willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext;
|
||||||
|
didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext;
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or
|
// For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or
|
||||||
// _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs.
|
// _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs.
|
||||||
if (currentContext && _willDisplayNodeContentWithRenderingContext) {
|
if (willDisplayNodeContentWithRenderingContext != nil) {
|
||||||
_willDisplayNodeContentWithRenderingContext(currentContext);
|
willDisplayNodeContentWithRenderingContext(currentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decide if we use a class or instance method to draw or display.
|
// Decide if we use a class or instance method to draw or display.
|
||||||
@@ -242,8 +258,8 @@
|
|||||||
isCancelled:isCancelledBlock isRasterizing:rasterizing];
|
isCancelled:isCancelledBlock isRasterizing:rasterizing];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentContext && _didDisplayNodeContentWithRenderingContext) {
|
if (didDisplayNodeContentWithRenderingContext != nil) {
|
||||||
_didDisplayNodeContentWithRenderingContext(currentContext);
|
didDisplayNodeContentWithRenderingContext(currentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldCreateGraphicsContext) {
|
if (shouldCreateGraphicsContext) {
|
||||||
@@ -264,12 +280,17 @@
|
|||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
__instanceLock__.lock();
|
||||||
|
|
||||||
if (_hierarchyState & ASHierarchyStateRasterized) {
|
if (_hierarchyState & ASHierarchyStateRasterized) {
|
||||||
|
__instanceLock__.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CALayer *layer = _layer;
|
||||||
|
|
||||||
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
// for async display, capture the current displaySentinel value to bail early when the job is executed if another is
|
// for async display, capture the current displaySentinel value to bail early when the job is executed if another is
|
||||||
// enqueued
|
// enqueued
|
||||||
// for sync display, do not support cancellation
|
// for sync display, do not support cancellation
|
||||||
@@ -306,10 +327,10 @@
|
|||||||
UIImage *image = (UIImage *)value;
|
UIImage *image = (UIImage *)value;
|
||||||
BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
|
BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
|
||||||
if (stretchable) {
|
if (stretchable) {
|
||||||
ASDisplayNodeSetupLayerContentsWithResizableImage(_layer, image);
|
ASDisplayNodeSetupLayerContentsWithResizableImage(layer, image);
|
||||||
} else {
|
} else {
|
||||||
_layer.contentsScale = self.contentsScale;
|
layer.contentsScale = self.contentsScale;
|
||||||
_layer.contents = (id)image.CGImage;
|
layer.contents = (id)image.CGImage;
|
||||||
}
|
}
|
||||||
[self didDisplayAsyncLayer:self.asyncLayer];
|
[self didDisplayAsyncLayer:self.asyncLayer];
|
||||||
}
|
}
|
||||||
@@ -323,7 +344,7 @@
|
|||||||
// while synchronizing the final application of the results to the layer's contents property (completionBlock).
|
// while synchronizing the final application of the results to the layer's contents property (completionBlock).
|
||||||
|
|
||||||
// First, look to see if we are expected to join a parent's transaction container.
|
// First, look to see if we are expected to join a parent's transaction container.
|
||||||
CALayer *containerLayer = _layer.asyncdisplaykit_parentTransactionContainer ? : _layer;
|
CALayer *containerLayer = layer.asyncdisplaykit_parentTransactionContainer ? : layer;
|
||||||
|
|
||||||
// In the case that a transaction does not yet exist (such as for an individual node outside of a container),
|
// In the case that a transaction does not yet exist (such as for an individual node outside of a container),
|
||||||
// this call will allocate the transaction and add it to _ASAsyncTransactionGroup.
|
// this call will allocate the transaction and add it to _ASAsyncTransactionGroup.
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat
|
|||||||
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes
|
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes
|
||||||
* size is invalidated and may need to result in a different size as the current calculated size.
|
* size is invalidated and may need to result in a different size as the current calculated size.
|
||||||
*/
|
*/
|
||||||
- (void)_locked_rootNodeDidInvalidateSize;
|
- (void)_rootNodeDidInvalidateSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called after measurement
|
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called after measurement
|
||||||
|
|||||||
@@ -200,9 +200,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
|||||||
/// Bitmask to check which methods an object overrides.
|
/// Bitmask to check which methods an object overrides.
|
||||||
@property (nonatomic, assign, readonly) ASDisplayNodeMethodOverrides methodOverrides;
|
@property (nonatomic, assign, readonly) ASDisplayNodeMethodOverrides methodOverrides;
|
||||||
|
|
||||||
// Swizzle to extend the builtin functionality with custom logic
|
|
||||||
- (BOOL)__shouldLoadViewOrLayer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked before a call to setNeedsLayout to the underlying view
|
* Invoked before a call to setNeedsLayout to the underlying view
|
||||||
*/
|
*/
|
||||||
@@ -218,11 +215,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
|||||||
*/
|
*/
|
||||||
- (void)__layout;
|
- (void)__layout;
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal method to set the supernode
|
|
||||||
*/
|
|
||||||
- (void)__setSupernode:(nullable ASDisplayNode *)supernode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method to add / replace / insert subnode and remove from supernode without checking if
|
* Internal method to add / replace / insert subnode and remove from supernode without checking if
|
||||||
* node has automaticallyManagesSubnodes set to YES.
|
* node has automaticallyManagesSubnodes set to YES.
|
||||||
@@ -241,7 +233,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
|||||||
- (void)__decrementVisibilityNotificationsDisabled;
|
- (void)__decrementVisibilityNotificationsDisabled;
|
||||||
|
|
||||||
/// Helper method to summarize whether or not the node run through the display process
|
/// Helper method to summarize whether or not the node run through the display process
|
||||||
- (BOOL)__implementsDisplay;
|
- (BOOL)_implementsDisplay;
|
||||||
|
|
||||||
/// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
|
/// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
|
||||||
- (void)displayImmediately;
|
- (void)displayImmediately;
|
||||||
|
|||||||
Reference in New Issue
Block a user