Skip to content

Commit

Permalink
[ASDisplayNode+Layout] Add check for orphaned nodes after layout tran…
Browse files Browse the repository at this point in the history
…sition 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: #335
  • Loading branch information
appleguy authored Jun 6, 2017
1 parent a9837f2 commit 00013aa
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
45 changes: 44 additions & 1 deletion Source/ASDisplayNode+Layout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -817,11 +817,54 @@ - (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition
}
}

- (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__);

Expand Down

0 comments on commit 00013aa

Please sign in to comment.