[ASDisplayNode+Layout] Add check for orphaned nodes after layout transition to clean up. (#336)

It is rare that this code has any effect, but I've discovered a case in which it occurs.

This task tracks moving this code to a DEBUG-only assertion: https://github.com/TextureGroup/Texture/issues/335
This commit is contained in:
appleguy 2017-06-06 12:38:37 -07:00 committed by GitHub
parent a9837f2dc8
commit 00013aadab
2 changed files with 45 additions and 1 deletions

View File

@ -1,6 +1,7 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- [ASDisplayNode+Layout] Add check for orphaned nodes after layout transition to clean up. #336. [Scott Goodson](https://github.com/appleguy)
- Fixed an issue where GIFs with placeholders never had their placeholders uncover the GIF. [Garrett Moon](https://github.com/garrettmoon)
- [Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga-powered layout calculation. [Scott Goodson](https://github.com/appleguy)
- Fixed an issue where calls to setNeedsDisplay and setNeedsLayout would stop working on loaded nodes. [Garrett Moon](https://github.com/garrettmoon)

View File

@ -817,11 +817,54 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
}
}
- (void)_assertSubnodeState
{
// Verify that any orphaned nodes are removed.
// This can occur in rare cases if main thread layout is flushed while a background layout is calculating.
if (self.automaticallyManagesSubnodes == NO) {
return;
}
NSArray *subnodes = [self subnodes];
NSArray *sublayouts = _calculatedDisplayNodeLayout->layout.sublayouts;
auto currentSubnodes = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality
capacity:subnodes.count];
auto layoutSubnodes = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality
capacity:sublayouts.count];;
for (ASDisplayNode *subnode in subnodes) {
[currentSubnodes addObject:subnode];
}
for (ASLayout *sublayout in sublayouts) {
id <ASLayoutElement> layoutElement = sublayout.layoutElement;
ASDisplayNodeAssert([layoutElement isKindOfClass:[ASDisplayNode class]],
@"All calculatedLayouts should be flattened and only contain nodes!");
[layoutSubnodes addObject:(ASDisplayNode *)layoutElement];
}
// Verify that all subnodes that occur in the current ASLayout tree are present in .subnodes array.
if ([layoutSubnodes isSubsetOfHashTable:currentSubnodes] == NO) {
// Note: This should be converted to an assertion after confirming it is rare.
NSLog(@"Warning: node's layout includes subnodes that have not been added: node = %@, subnodes = %@, subnodes in layout = %@", self, currentSubnodes, layoutSubnodes);
}
// Verify that everything in the .subnodes array is present in the ASLayout tree (and correct it if not).
[currentSubnodes minusHashTable:layoutSubnodes];
for (ASDisplayNode *orphanedSubnode in currentSubnodes) {
NSLog(@"Automatically removing orphaned subnode %@, from parent %@", orphanedSubnode, self);
[orphanedSubnode removeFromSupernode];
}
}
- (void)_pendingLayoutTransitionDidComplete
{
[self _assertSubnodeState];
// Subclass hook
[self calculatedLayoutDidChange];
// Grab lock after calling out to subclass
ASDN::MutexLocker l(__instanceLock__);