Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perform relayoutAllNodes (rotation, window resize) concurrently #1446

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Source/Details/ASCollectionElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@

NS_ASSUME_NONNULL_BEGIN

/// Copy returns self. Only here for dictionary use.
AS_SUBCLASSING_RESTRICTED
@interface ASCollectionElement : NSObject
@interface ASCollectionElement : NSObject <NSCopying>

@property (nullable, nonatomic, copy, readonly) NSString *supplementaryElementKind;
@property (nonatomic) ASSizeRange constrainedSize;
Expand Down
5 changes: 5 additions & 0 deletions Source/Details/ASCollectionElement.mm
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,9 @@ - (void)setTraitCollection:(ASPrimitiveTraitCollection)traitCollection
}
}

- (id)copyWithZone:(NSZone *)zone
{
return self;
}

@end
43 changes: 22 additions & 21 deletions Source/Details/ASDataController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -890,27 +890,28 @@ - (void)_relayoutAllNodes
_pendingMap = [newMap copy];
_visibleMap = _pendingMap;

for (ASCollectionElement *element in _visibleMap) {
// Ignore this element if it is no longer in the latest data. It is still recognized in the UIKit world but will be deleted soon.
NSIndexPath *indexPathInPendingMap = [_pendingMap indexPathForElement:element];
if (indexPathInPendingMap == nil) {
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this case uncommon and irrelevant now that we relayout concurrently?

}

NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind;
ASSizeRange newConstrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPathInPendingMap];

if (ASSizeRangeHasSignificantArea(newConstrainedSize)) {
element.constrainedSize = newConstrainedSize;

// Node may not be allocated yet (e.g node virtualization or same size optimization)
// Call context.nodeIfAllocated here to avoid premature node allocation and layout
ASCellNode *node = element.nodeIfAllocated;
if (node) {
[self _layoutNode:node withConstrainedSize:newConstrainedSize];
}
}
}
// First update size constraints on the main thread.
NSDictionary<ASCollectionElement *, NSIndexPath *> *elementToIndexPath = _visibleMap.elementToIndexPath;
[elementToIndexPath
enumerateKeysAndObjectsUsingBlock:^(ASCollectionElement *element, NSIndexPath *indexPath,
__unused BOOL *stop) {
element.constrainedSize = [self constrainedSizeForNodeOfKind:(element.supplementaryElementKind
?: ASDataControllerRowNodeKind)
atIndexPath:indexPath];
}];

// Then concurrently synchronously ensure every node is measured against new constraints.
[elementToIndexPath
enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(ASCollectionElement *element, NSIndexPath *indexPath,
__unused BOOL *stop) {
const ASSizeRange sizeRange = element.constrainedSize;
if (ASSizeRangeHasSignificantArea(sizeRange)) {
if (ASCellNode *node = element.nodeIfAllocated) {
[self _layoutNode:node withConstrainedSize:sizeRange];
}
}
}];
}

# pragma mark - ASPrimitiveTraitCollection
Expand Down
5 changes: 5 additions & 0 deletions Source/Details/ASElementMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ AS_SUBCLASSING_RESTRICTED
*/
@property (copy, readonly) NSArray<ASCollectionElement *> *itemElements;

/**
* All the elements in the map, in NSDictionary form. O(1)
*/
@property (readonly) NSDictionary<ASCollectionElement *, NSIndexPath *> *elementToIndexPath;

/**
* Returns the index path that corresponds to the same element in @c map at the given @c indexPath.
* O(1) for items, fast O(N) for sections.
Expand Down
15 changes: 6 additions & 9 deletions Source/Details/ASElementMap.mm
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ @interface ASElementMap () <ASDescriptionProvider>

@property (nonatomic, readonly) NSArray<ASSection *> *sections;

// Element -> IndexPath
@property (nonatomic, readonly) NSMapTable<ASCollectionElement *, NSIndexPath *> *elementToIndexPathMap;

// The items, in a 2D array
@property (nonatomic, readonly) ASCollectionElementTwoDimensionalArray *sectionsOfItems;

Expand All @@ -47,20 +44,20 @@ - (instancetype)initWithSections:(NSArray<ASSection *> *)sections items:(ASColle
_supplementaryElements = [[NSDictionary alloc] initWithDictionary:supplementaryElements copyItems:YES];

// Setup our index path map
_elementToIndexPathMap = [NSMapTable mapTableWithKeyOptions:(NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableCopyIn];
auto mElementToIndexPath = [[NSMutableDictionary<ASCollectionElement *, NSIndexPath *> alloc] init];
NSInteger s = 0;
for (NSArray *section in _sectionsOfItems) {
NSInteger i = 0;
for (ASCollectionElement *element in section) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s];
[_elementToIndexPathMap setObject:indexPath forKey:element];
[mElementToIndexPath setObject:indexPath forKey:element];
i++;
}
s++;
}
for (NSDictionary *supplementariesForKind in [_supplementaryElements objectEnumerator]) {
[supplementariesForKind enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *_Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) {
[_elementToIndexPathMap setObject:indexPath forKey:element];
[mElementToIndexPath setObject:indexPath forKey:element];
}];
}
}
Expand All @@ -69,7 +66,7 @@ - (instancetype)initWithSections:(NSArray<ASSection *> *)sections items:(ASColle

- (NSUInteger)count
{
return _elementToIndexPathMap.count;
return _elementToIndexPath.count;
}

- (NSArray<NSIndexPath *> *)itemIndexPaths
Expand Down Expand Up @@ -112,7 +109,7 @@ - (NSInteger)numberOfItemsInSection:(NSInteger)section

- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element
{
return element ? [_elementToIndexPathMap objectForKey:element] : nil;
return element ? [_elementToIndexPath objectForKey:element] : nil;
}

- (nullable NSIndexPath *)indexPathForElementIfCell:(ASCollectionElement *)element
Expand Down Expand Up @@ -196,7 +193,7 @@ - (id)mutableCopyWithZone:(NSZone *)zone

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable __unsafe_unretained [])buffer count:(NSUInteger)len
{
return [_elementToIndexPathMap countByEnumeratingWithState:state objects:buffer count:len];
return [_elementToIndexPath countByEnumeratingWithState:state objects:buffer count:len];
}

- (NSString *)smallDescription
Expand Down