mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[ASDisplayNode] Ensure ASHierarchyState propagation does not jump discontinuities in the node hierarchy. (#2263)
Scenario: An ASCollectionNode is a subnode of an ASCellNode. A layout transition is started, resulting in the removal of the ASCollectionNode as a subnode. As it is removed, the hierarchy state is cleared - including the "range managed" bit - on the ASCollectionNode. However, the deep recursion traverses the layer hierarchy too, and clears this bit on the ASCellNodes inside the ASCollectionNode. A moment later, the collection performs its final ASRangeController update to mark its cells as invisible and free memory. Then an assertion is triggered in ASRangeController, because it is operating on nodes that do not have the "range managed" bit set. It turns out that ASInterfaceState also propogates in this way, but that behavior is efficient and beneficial in its current configuration (it assists how multi-dimensional preloading works). However, hierarchy state should never need to jump discontinuities in the node hierarchy. For now, disabling that case and will revisit other use cases soon.
This commit is contained in:
@@ -39,7 +39,7 @@ extern ASDisplayNode *ASViewToDisplayNode(UIView *view)
|
||||
return view.asyncdisplaykit_node;
|
||||
}
|
||||
|
||||
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node))
|
||||
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node))
|
||||
{
|
||||
if (!node) {
|
||||
ASDisplayNodeCAssertNotNil(layer, @"Cannot recursively perform with nil node and nil layer");
|
||||
@@ -50,19 +50,19 @@ extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *
|
||||
if (node) {
|
||||
block(node);
|
||||
}
|
||||
if (!layer && [node isNodeLoaded] && ASDisplayNodeThreadIsMain()) {
|
||||
if (traverseSublayers && !layer && [node isNodeLoaded] && ASDisplayNodeThreadIsMain()) {
|
||||
layer = node.layer;
|
||||
}
|
||||
|
||||
if (layer && node.shouldRasterizeDescendants == NO) {
|
||||
if (traverseSublayers && layer && node.shouldRasterizeDescendants == NO) {
|
||||
/// NOTE: The docs say `sublayers` returns a copy, but it does not.
|
||||
/// See: http://stackoverflow.com/questions/14854480/collection-calayerarray-0x1ed8faa0-was-mutated-while-being-enumerated
|
||||
for (CALayer *sublayer in [[layer sublayers] copy]) {
|
||||
ASDisplayNodePerformBlockOnEveryNode(sublayer, nil, block);
|
||||
ASDisplayNodePerformBlockOnEveryNode(sublayer, nil, traverseSublayers, block);
|
||||
}
|
||||
} else if (node) {
|
||||
for (ASDisplayNode *subnode in [node subnodes]) {
|
||||
ASDisplayNodePerformBlockOnEveryNode(nil, subnode, block);
|
||||
ASDisplayNodePerformBlockOnEveryNode(nil, subnode, traverseSublayers, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,10 +86,10 @@ extern void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^b
|
||||
}
|
||||
}
|
||||
|
||||
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
|
||||
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node))
|
||||
{
|
||||
for (ASDisplayNode *subnode in node.subnodes) {
|
||||
ASDisplayNodePerformBlockOnEveryNode(nil, subnode, block);
|
||||
ASDisplayNodePerformBlockOnEveryNode(nil, subnode, YES, block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user