Replace NSMutableSet with NSHashTable when Appropriate #trivial (#321)

* Use NSHashTable to avoid needless -hash and -isEqual: calls

* Mark debug-only methods as such for clarity

* Address feedback
This commit is contained in:
Adlai Holler
2017-06-05 16:33:37 -07:00
committed by GitHub
parent 4a97c4e53c
commit a9837f2dc8
13 changed files with 66 additions and 45 deletions

View File

@@ -90,8 +90,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
ASCollectionViewLayoutController *_layoutController; ASCollectionViewLayoutController *_layoutController;
id<ASCollectionViewLayoutInspecting> _defaultLayoutInspector; id<ASCollectionViewLayoutInspecting> _defaultLayoutInspector;
__weak id<ASCollectionViewLayoutInspecting> _layoutInspector; __weak id<ASCollectionViewLayoutInspecting> _layoutInspector;
NSMutableSet *_cellsForVisibilityUpdates; NSHashTable<_ASCollectionViewCell *> *_cellsForVisibilityUpdates;
NSMutableSet *_cellsForLayoutUpdates; NSHashTable<ASCellNode *> *_cellsForLayoutUpdates;
id<ASCollectionViewLayoutFacilitatorProtocol> _layoutFacilitator; id<ASCollectionViewLayoutFacilitatorProtocol> _layoutFacilitator;
CGFloat _leadingScreensForBatching; CGFloat _leadingScreensForBatching;
BOOL _inverted; BOOL _inverted;
@@ -299,8 +299,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
_registeredSupplementaryKinds = [NSMutableSet set]; _registeredSupplementaryKinds = [NSMutableSet set];
_visibleElements = [[NSCountedSet alloc] init]; _visibleElements = [[NSCountedSet alloc] init];
_cellsForVisibilityUpdates = [NSMutableSet set]; _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
_cellsForLayoutUpdates = [NSMutableSet set]; _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
[self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier];
@@ -1827,9 +1827,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
return result; return result;
} }
- (NSArray<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController - (NSHashTable<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController
{ {
return _visibleElements.allObjects; return ASPointerTableByFlatMapping(_visibleElements, id element, element);
} }
- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController - (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController

View File

@@ -854,10 +854,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
#pragma mark - Layout #pragma mark - Layout
#if DEBUG
#define AS_DEDUPE_LAYOUT_SPEC_TREE 1
#endif
// At most a layoutSpecBlock or one of the three layout methods is overridden // At most a layoutSpecBlock or one of the three layout methods is overridden
#define __ASDisplayNodeCheckForLayoutMethodOverrides \ #define __ASDisplayNodeCheckForLayoutMethodOverrides \
ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ ASDisplayNodeAssert(_layoutSpecBlock != NULL || \
@@ -1002,7 +998,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement; ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement;
#if AS_DEDUPE_LAYOUT_SPEC_TREE #if AS_DEDUPE_LAYOUT_SPEC_TREE
NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; NSHashTable *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree];
if (duplicateElements.count > 0) { if (duplicateElements.count > 0) {
ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements);
// Use an empty layout spec to avoid crashes // Use an empty layout spec to avoid crashes

View File

@@ -191,13 +191,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
CGFloat _nodesConstrainedWidth; CGFloat _nodesConstrainedWidth;
BOOL _queuedNodeHeightUpdate; BOOL _queuedNodeHeightUpdate;
BOOL _isDeallocating; BOOL _isDeallocating;
NSMutableSet *_cellsForVisibilityUpdates; NSHashTable<_ASTableViewCell *> *_cellsForVisibilityUpdates;
// CountedSet because UIKit may display the same element in multiple cells e.g. during animations. // CountedSet because UIKit may display the same element in multiple cells e.g. during animations.
NSCountedSet<ASCollectionElement *> *_visibleElements; NSCountedSet<ASCollectionElement *> *_visibleElements;
BOOL _remeasuringCellNodes; BOOL _remeasuringCellNodes;
NSMutableSet *_cellsForLayoutUpdates; NSHashTable<ASCellNode *> *_cellsForLayoutUpdates;
// See documentation on same property in ASCollectionView // See documentation on same property in ASCollectionView
BOOL _hasEverCheckedForBatchFetchingDueToUpdate; BOOL _hasEverCheckedForBatchFetchingDueToUpdate;
@@ -309,8 +309,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
if (!(self = [super initWithFrame:frame style:style])) { if (!(self = [super initWithFrame:frame style:style])) {
return nil; return nil;
} }
_cellsForVisibilityUpdates = [NSMutableSet set]; _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
_cellsForLayoutUpdates = [NSMutableSet set]; _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
if (!dataControllerClass) { if (!dataControllerClass) {
dataControllerClass = [[self class] dataControllerClass]; dataControllerClass = [[self class] dataControllerClass];
} }
@@ -686,7 +686,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (NSArray<ASCellNode *> *)visibleNodes - (NSArray<ASCellNode *> *)visibleNodes
{ {
NSArray<ASCollectionElement *> *elements = [self visibleElementsForRangeController:_rangeController]; auto elements = [self visibleElementsForRangeController:_rangeController];
return ASArrayByFlatMapping(elements, ASCollectionElement *e, e.node); return ASArrayByFlatMapping(elements, ASCollectionElement *e, e.node);
} }
@@ -1457,9 +1457,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
return _rangeController; return _rangeController;
} }
- (NSArray<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController - (NSHashTable<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController
{ {
return _visibleElements.allObjects; return ASPointerTableByFlatMapping(_visibleElements, id element, element);
} }
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController - (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController

View File

@@ -235,6 +235,20 @@
s; \ s; \
}) })
/**
* Create a new ObjectPointerPersonality NSHashTable by mapping `collection` over `work`, ignoring nil.
*/
#define ASPointerTableByFlatMapping(collection, decl, work) ({ \
NSHashTable *t = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; \
for (decl in collection) {\
id result = work; \
if (result != nil) { \
[t addObject:result]; \
} \
} \
t; \
})
/** /**
* Create a new array by mapping `collection` over `work`, ignoring nil. * Create a new array by mapping `collection` over `work`, ignoring nil.
*/ */

View File

@@ -36,9 +36,9 @@ ASDISPLAYNODE_EXTERN_C_END
@interface ASAbstractLayoutController (Unavailable) @interface ASAbstractLayoutController (Unavailable)
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable; - (NSHashTable *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable;
- (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet * _Nullable * _Nullable)displaySet preloadSet:(NSSet * _Nullable * _Nullable)preloadSet __unavailable; - (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet __unavailable;
@end @end

View File

@@ -174,13 +174,13 @@ extern CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTu
#pragma mark - Abstract Index Path Range Support #pragma mark - Abstract Index Path Range Support
- (NSSet<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map - (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{ {
ASDisplayNodeAssertNotSupported(); ASDisplayNodeAssertNotSupported();
return nil; return nil;
} }
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map - (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{ {
ASDisplayNodeAssertNotSupported(); ASDisplayNodeAssertNotSupported();
} }

View File

@@ -54,14 +54,14 @@ typedef struct ASRangeGeometry ASRangeGeometry;
return self; return self;
} }
- (NSSet<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map - (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{ {
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters]; CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters];
return [self elementsWithinRangeBounds:rangeBounds map:map]; return [self elementsWithinRangeBounds:rangeBounds map:map];
} }
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map - (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{ {
if (displaySet == NULL || preloadSet == NULL) { if (displaySet == NULL || preloadSet == NULL) {
return; return;
@@ -74,9 +74,10 @@ typedef struct ASRangeGeometry ASRangeGeometry;
CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds); CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds);
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds]; NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds];
NSInteger count = layoutAttributes.count;
NSMutableSet<ASCollectionElement *> *display = [NSMutableSet setWithCapacity:layoutAttributes.count]; __auto_type display = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
NSMutableSet<ASCollectionElement *> *preload = [NSMutableSet setWithCapacity:layoutAttributes.count]; __auto_type preload = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) { for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
// Manually filter out elements that don't intersect the range bounds. // Manually filter out elements that don't intersect the range bounds.
@@ -105,10 +106,10 @@ typedef struct ASRangeGeometry ASRangeGeometry;
return; return;
} }
- (NSSet<ASCollectionElement *> *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map - (NSHashTable<ASCollectionElement *> *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map
{ {
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds]; NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
NSMutableSet<ASCollectionElement *> *elementSet = [NSMutableSet setWithCapacity:layoutAttributes.count]; NSHashTable<ASCollectionElement *> *elementSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:layoutAttributes.count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) { for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
// Manually filter out elements that don't intersect the range bounds. // Manually filter out elements that don't intersect the range bounds.

View File

@@ -41,9 +41,9 @@ ASDISPLAYNODE_EXTERN_C_END
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
- (NSSet<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map; - (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map;
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet<ASCollectionElement *> * _Nullable * _Nullable)displaySet preloadSet:(NSSet<ASCollectionElement *> * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map; - (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable<ASCollectionElement *> * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map;
@optional @optional

View File

@@ -114,9 +114,9 @@ AS_SUBCLASSING_RESTRICTED
/** /**
* @param rangeController Sender. * @param rangeController Sender.
* *
* @return an array of elements corresponding to the data currently visible onscreen (i.e., the visible range). * @return an table of elements corresponding to the data currently visible onscreen (i.e., the visible range).
*/ */
- (NSArray<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController; - (nullable NSHashTable<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController;
/** /**
* @param rangeController Sender. * @param rangeController Sender.

View File

@@ -216,7 +216,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
// TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges // TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
// Example: ... = [_layoutController indexPathsForScrolling:scrollDirection rangeType:ASLayoutRangeTypeVisible]; // Example: ... = [_layoutController indexPathsForScrolling:scrollDirection rangeType:ASLayoutRangeTypeVisible];
NSSet<ASCollectionElement *> *visibleElements = [NSSet setWithArray:[_dataSource visibleElementsForRangeController:self]]; auto visibleElements = [_dataSource visibleElementsForRangeController:self];
NSHashTable *newVisibleNodes = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; NSHashTable *newVisibleNodes = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
if (visibleElements.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... if (visibleElements.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
@@ -255,14 +255,14 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
// Check if both Display and Preload are unique. If they are, we load them with a single fetch from the layout controller for performance. // Check if both Display and Preload are unique. If they are, we load them with a single fetch from the layout controller for performance.
BOOL optimizedLoadingOfBothRanges = (equalDisplayPreload == NO && equalDisplayVisible == NO && emptyDisplayRange == NO); BOOL optimizedLoadingOfBothRanges = (equalDisplayPreload == NO && equalDisplayVisible == NO && emptyDisplayRange == NO);
NSSet<ASCollectionElement *> *displayElements = nil; NSHashTable<ASCollectionElement *> *displayElements = nil;
NSSet<ASCollectionElement *> *preloadElements = nil; NSHashTable<ASCollectionElement *> *preloadElements = nil;
if (optimizedLoadingOfBothRanges) { if (optimizedLoadingOfBothRanges) {
[_layoutController allElementsForScrolling:scrollDirection rangeMode:rangeMode displaySet:&displayElements preloadSet:&preloadElements map:map]; [_layoutController allElementsForScrolling:scrollDirection rangeMode:rangeMode displaySet:&displayElements preloadSet:&preloadElements map:map];
} else { } else {
if (emptyDisplayRange == YES) { if (emptyDisplayRange == YES) {
displayElements = [NSSet set]; displayElements = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
} if (equalDisplayVisible == YES) { } if (equalDisplayVisible == YES) {
displayElements = visibleElements; displayElements = visibleElements;
} else { } else {

View File

@@ -38,17 +38,17 @@
#pragma mark - ASLayoutController #pragma mark - ASLayoutController
- (NSSet<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map - (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{ {
CGRect bounds = _tableView.bounds; CGRect bounds = _tableView.bounds;
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGRect rangeBounds = CGRectExpandToRangeWithScrollableDirections(bounds, tuningParameters, ASScrollDirectionVerticalDirections, scrollDirection); CGRect rangeBounds = CGRectExpandToRangeWithScrollableDirections(bounds, tuningParameters, ASScrollDirectionVerticalDirections, scrollDirection);
NSArray *array = [_tableView indexPathsForRowsInRect:rangeBounds]; NSArray *array = [_tableView indexPathsForRowsInRect:rangeBounds];
return ASSetByFlatMapping(array, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]); return ASPointerTableByFlatMapping(array, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]);
} }
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map - (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{ {
if (displaySet == NULL || preloadSet == NULL) { if (displaySet == NULL || preloadSet == NULL) {
return; return;

View File

@@ -170,11 +170,12 @@ ASLayoutElementStyleExtensibilityForwarding
#pragma mark - Framework Private #pragma mark - Framework Private
- (nullable NSSet<id<ASLayoutElement>> *)findDuplicatedElementsInSubtree #if AS_DEDUPE_LAYOUT_SPEC_TREE
- (nullable NSHashTable<id<ASLayoutElement>> *)findDuplicatedElementsInSubtree
{ {
NSMutableSet *result = nil; NSHashTable *result = nil;
NSUInteger count = 0; NSUInteger count = 0;
[self _findDuplicatedElementsInSubtreeWithWorkingSet:[[NSMutableSet alloc] init] workingCount:&count result:&result]; [self _findDuplicatedElementsInSubtreeWithWorkingSet:[NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality] workingCount:&count result:&result];
return result; return result;
} }
@@ -185,7 +186,7 @@ ASLayoutElementStyleExtensibilityForwarding
* @param workingCount The current count of the set for use in the recursion. * @param workingCount The current count of the set for use in the recursion.
* @param result The set into which to put the result. This initially points to @c nil to save time if no duplicates exist. * @param result The set into which to put the result. This initially points to @c nil to save time if no duplicates exist.
*/ */
- (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSMutableSet<id<ASLayoutElement>> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSMutableSet<id<ASLayoutElement>> * _Nullable *)result - (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSHashTable<id<ASLayoutElement>> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSHashTable<id<ASLayoutElement>> * _Nullable *)result
{ {
Class layoutSpecClass = [ASLayoutSpec class]; Class layoutSpecClass = [ASLayoutSpec class];
@@ -200,7 +201,7 @@ ASLayoutElementStyleExtensibilityForwarding
BOOL objectAlreadyExisted = (newCount != oldCount + 1); BOOL objectAlreadyExisted = (newCount != oldCount + 1);
if (objectAlreadyExisted) { if (objectAlreadyExisted) {
if (*result == nil) { if (*result == nil) {
*result = [[NSMutableSet alloc] init]; *result = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
} }
[*result addObject:child]; [*result addObject:child];
} else { } else {
@@ -212,6 +213,7 @@ ASLayoutElementStyleExtensibilityForwarding
} }
} }
} }
#endif
#pragma mark - Debugging #pragma mark - Debugging

View File

@@ -18,6 +18,12 @@
#import <AsyncDisplayKit/ASInternalHelpers.h> #import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASThread.h> #import <AsyncDisplayKit/ASThread.h>
#if DEBUG
#define AS_DEDUPE_LAYOUT_SPEC_TREE 1
#else
#define AS_DEDUPE_LAYOUT_SPEC_TREE 0
#endif
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface ASLayoutSpec() { @interface ASLayoutSpec() {
@@ -27,10 +33,12 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableArray *_childrenArray; NSMutableArray *_childrenArray;
} }
#if AS_DEDUPE_LAYOUT_SPEC_TREE
/** /**
* Recursively search the subtree for elements that occur more than once. * Recursively search the subtree for elements that occur more than once.
*/ */
- (nullable NSSet<id<ASLayoutElement>> *)findDuplicatedElementsInSubtree; - (nullable NSHashTable<id<ASLayoutElement>> *)findDuplicatedElementsInSubtree;
#endif
@end @end