Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit e4c6a49

Browse files
committed
Merge pull request #1441 from facebook/Invisibility
[ASDisplayNode] Alternative implementation of interfaceState -> invisible that avoids a subclass call from -dealloc.
2 parents 82a4f5c + c6093cf commit e4c6a49

1 file changed

Lines changed: 31 additions & 14 deletions

File tree

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,9 @@ - (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDis
337337
- (void)dealloc
338338
{
339339
ASDisplayNodeAssertMainThread();
340+
// Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes.
341+
ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating; interfaceState: %lu, %@", (unsigned long)_interfaceState, self);
340342

341-
self.interfaceState &= ~ASInterfaceStateVisible;
342-
343343
self.asyncLayer.asyncDelegate = nil;
344344
_view.asyncdisplaykit_node = nil;
345345
_layer.asyncdisplaykit_node = nil;
@@ -829,7 +829,7 @@ - (BOOL)displaysAsynchronously
829829
- (BOOL)_displaysAsynchronously
830830
{
831831
ASDisplayNodeAssertThreadAffinity(self);
832-
return self.isSynchronous == NO && _flags.displaysAsynchronously;
832+
return _flags.synchronous == NO && _flags.displaysAsynchronously;
833833
}
834834

835835
- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously
@@ -1545,10 +1545,11 @@ - (void)__enterHierarchy
15451545
// Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
15461546
ASDN::MutexLocker l(_propertyLock);
15471547

1548-
if (!self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
1549-
self.inHierarchy = YES;
1548+
if (!_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
15501549
_flags.isEnteringHierarchy = YES;
1551-
if (self.shouldRasterizeDescendants) {
1550+
_flags.isInHierarchy = YES;
1551+
1552+
if (_flags.shouldRasterizeDescendants) {
15521553
// Nodes that are descendants of a rasterized container do not have views or layers, and so cannot receive visibility notifications directly via orderIn/orderOut CALayer actions. Manually send visibility notifications to rasterized descendants.
15531554
[self _recursiveWillEnterHierarchy];
15541555
} else {
@@ -1568,7 +1569,7 @@ - (void)__enterHierarchy
15681569
[self _setupPlaceholderLayerIfNeeded];
15691570
_placeholderLayer.opacity = 1.0;
15701571
[CATransaction commit];
1571-
[self.layer addSublayer:_placeholderLayer];
1572+
[layer addSublayer:_placeholderLayer];
15721573
}
15731574
}
15741575
}
@@ -1582,18 +1583,34 @@ - (void)__exitHierarchy
15821583
// Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
15831584
ASDN::MutexLocker l(_propertyLock);
15841585

1585-
if (self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
1586-
self.inHierarchy = NO;
1586+
if (_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
1587+
_flags.isExitingHierarchy = YES;
1588+
_flags.isInHierarchy = NO;
15871589

15881590
[self.asyncLayer cancelAsyncDisplay];
15891591

1590-
_flags.isExitingHierarchy = YES;
1591-
if (self.shouldRasterizeDescendants) {
1592+
if (_flags.shouldRasterizeDescendants) {
15921593
// Nodes that are descendants of a rasterized container do not have views or layers, and so cannot receive visibility notifications directly via orderIn/orderOut CALayer actions. Manually send visibility notifications to rasterized descendants.
15931594
[self _recursiveDidExitHierarchy];
1594-
} else {
1595-
[self didExitHierarchy];
15961595
}
1596+
1597+
// This case is important when tearing down hierarchies. We must deliver a visibilityDidChange:NO callback, as part our API guarantee that this method can be used for
1598+
// things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail.
1599+
// Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the
1600+
// same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed).
1601+
// TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer
1602+
// integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call.
1603+
1604+
if (ASInterfaceStateIncludesVisible(_interfaceState)) {
1605+
dispatch_async(dispatch_get_main_queue(), ^{
1606+
ASDN::MutexLocker l(_propertyLock);
1607+
if (!_flags.isInHierarchy && ASInterfaceStateIncludesVisible(_interfaceState)) {
1608+
self.interfaceState = (_interfaceState & ~ASInterfaceStateVisible);
1609+
}
1610+
});
1611+
}
1612+
1613+
[self didExitHierarchy];
15971614
_flags.isExitingHierarchy = NO;
15981615
}
15991616
}
@@ -1719,7 +1736,7 @@ - (void)_pendingNodeDidDisplay:(ASDisplayNode *)node
17191736
// Helper method to summarize whether or not the node run through the display process
17201737
- (BOOL)__implementsDisplay
17211738
{
1722-
return _flags.implementsDrawRect || _flags.implementsImageDisplay || self.shouldRasterizeDescendants || _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
1739+
return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.shouldRasterizeDescendants || _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
17231740
}
17241741

17251742
- (BOOL)placeholderShouldPersist

0 commit comments

Comments
 (0)