From a0e67a8b1527d7b4a877ac0d3b126295e5658a64 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Fri, 10 Mar 2017 06:33:30 -0800 Subject: [PATCH] ASMultidimensionalArrayUtils -> ASTwoDimensionalArrayUtils (#3157) * Requery subarray count less in ASMultidimensionalArrayUtils * Simplify (gut) ASMultidimensionalArrayUtils * Remove weird change * Add bounds-checking for graceful failure --- AsyncDisplayKit.xcodeproj/project.pbxproj | 16 +- Source/Details/ASDataController.mm | 2 +- Source/Details/ASRangeController.mm | 2 +- Source/Private/ASElementMap.m | 2 +- Source/Private/ASMultidimensionalArrayUtils.h | 80 ------ .../Private/ASMultidimensionalArrayUtils.mm | 242 ------------------ Source/Private/ASMutableElementMap.m | 5 +- Source/Private/ASTwoDimensionalArrayUtils.h | 48 ++++ Source/Private/ASTwoDimensionalArrayUtils.m | 94 +++++++ 9 files changed, 155 insertions(+), 336 deletions(-) delete mode 100644 Source/Private/ASMultidimensionalArrayUtils.h delete mode 100644 Source/Private/ASMultidimensionalArrayUtils.mm create mode 100644 Source/Private/ASTwoDimensionalArrayUtils.h create mode 100644 Source/Private/ASTwoDimensionalArrayUtils.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 980a2968b1..9df33d23a1 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -11,8 +11,8 @@ 044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = 044285051BAA63FE00D16268 /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */ = {isa = PBXBuildFile; fileRef = 044285061BAA63FE00D16268 /* ASBatchFetching.m */; }; - 0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0442850B1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 044285101BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0442850C1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm */; }; + 0442850E1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0442850B1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 044285101BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 0442850C1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m */; }; 052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 052EE0651A159FEF002C6279 /* ASMultiplexImageNodeTests.m */; }; 052EE06B1A15A0D8002C6279 /* TestResources in Resources */ = {isa = PBXBuildFile; fileRef = 052EE06A1A15A0D8002C6279 /* TestResources */; }; 056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.m */; }; @@ -390,8 +390,8 @@ 044285011BAA3CC700D16268 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 044285051BAA63FE00D16268 /* ASBatchFetching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchFetching.h; sourceTree = ""; }; 044285061BAA63FE00D16268 /* ASBatchFetching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetching.m; sourceTree = ""; }; - 0442850B1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMultidimensionalArrayUtils.h; sourceTree = ""; }; - 0442850C1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMultidimensionalArrayUtils.mm; sourceTree = ""; }; + 0442850B1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTwoDimensionalArrayUtils.h; sourceTree = ""; }; + 0442850C1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTwoDimensionalArrayUtils.m; sourceTree = ""; }; 0516FA3A1A15563400B4EBED /* ASAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASAvailability.h; sourceTree = ""; }; 0516FA3B1A15563400B4EBED /* ASLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLog.h; sourceTree = ""; }; 0516FA3E1A1563D200B4EBED /* ASMultiplexImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMultiplexImageNode.h; sourceTree = ""; }; @@ -1192,8 +1192,8 @@ ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.m */, E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */, E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */, - 0442850B1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h */, - 0442850C1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm */, + 0442850B1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.h */, + 0442850C1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m */, CC3B20811C3F76D600798563 /* ASPendingStateController.h */, CC3B20821C3F76D600798563 /* ASPendingStateController.mm */, CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */, @@ -1504,7 +1504,7 @@ CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */, 690ED5961E36D118000627C0 /* ASControlNode+tvOS.h in Headers */, 695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */, - 0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, + 0442850E1BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.h in Headers */, DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, AC6145411D8AFAE8003D62A2 /* ASSection.h in Headers */, @@ -1906,7 +1906,7 @@ B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */, B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */, 34EFC76D1B701CF100AD841F /* ASOverlayLayoutSpec.mm in Sources */, - 044285101BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */, + 044285101BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, 0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */, 68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */, diff --git a/Source/Details/ASDataController.mm b/Source/Details/ASDataController.mm index 933cfba256..0c4b37df06 100644 --- a/Source/Details/ASDataController.mm +++ b/Source/Details/ASDataController.mm @@ -15,7 +15,7 @@ #import #import #import -#import +#import #import #import #import diff --git a/Source/Details/ASRangeController.mm b/Source/Details/ASRangeController.mm index b0e657c05f..024c99384b 100644 --- a/Source/Details/ASRangeController.mm +++ b/Source/Details/ASRangeController.mm @@ -18,7 +18,7 @@ #import // Required for interfaceState and hierarchyState setter methods. #import #import -#import +#import #import #import diff --git a/Source/Private/ASElementMap.m b/Source/Private/ASElementMap.m index 6879ef2b28..e3ce884b20 100644 --- a/Source/Private/ASElementMap.m +++ b/Source/Private/ASElementMap.m @@ -9,7 +9,7 @@ #import "ASElementMap.h" #import #import -#import +#import #import #import #import diff --git a/Source/Private/ASMultidimensionalArrayUtils.h b/Source/Private/ASMultidimensionalArrayUtils.h deleted file mode 100644 index eb2ab924e9..0000000000 --- a/Source/Private/ASMultidimensionalArrayUtils.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// ASMultidimensionalArrayUtils.h -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import - -#import - - -/** - * Helper class for operation on multidimensional array, where the object of array may be an object or an array. - */ - -ASDISPLAYNODE_EXTERN_C_BEGIN - -/** - * Deep mutable copy of an array that contains arrays, which contain objects. It will go one level deep into the array to copy. - * This method is substantially faster than the generalized version, e.g. about 10x faster, so use it whenever it fits the need. - */ -extern NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) AS_WARN_UNUSED_RESULT; - -/** - * Deep mutable copy of multidimensional array. This is completely generalized and supports copying mixed-depth arrays, - * where some subarrays might contain both elements and other subarrays. It will recursively do the multiple copy for each subarray. - */ -extern NSObject *ASMultidimensionalArrayDeepMutableCopy(NSObject *obj) AS_WARN_UNUSED_RESULT; - -/** - * Insert the elements into the mutable multidimensional array at given index paths. - */ -extern void ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths, NSArray *elements); - -/** - * Delete the elements of the mutable multidimensional array at given index paths - */ -extern void ASDeleteElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths); - -/** - * Find the elements of the mutable multidimensional array at given index paths. - */ -extern NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) AS_WARN_UNUSED_RESULT; - -/** - * Return all the index paths of mutable multidimensional array at given index set, in ascending order. - */ -extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensionalArray, NSIndexSet *indexSet) AS_WARN_UNUSED_RESULT; - -/** - * Moves the object at `sourceIndexPath` to `destinationIndexPath`. - */ -extern void ASMoveElementInTwoDimensionalArray(NSMutableArray *mutableArray, NSIndexPath *sourceIndexPath, NSIndexPath *destinationIndexPath); - -/** - * Return the index paths of the given multidimensional array that are present in the given index paths array. - */ -extern NSArray *ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths(NSArray *multidimensionalArray, NSArray *indexPaths) AS_WARN_UNUSED_RESULT; - -/** - * Return all the index paths of a two-dimensional array, in ascending order. - */ -extern NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) AS_WARN_UNUSED_RESULT; - -/** - * Return all the index paths of a multidimensional array, in ascending order. - */ -extern NSArray *ASIndexPathsForMultidimensionalArray(NSArray *MultidimensionalArray) AS_WARN_UNUSED_RESULT; - -/** - * Attempt to get the object at the given index path. Returns @c nil if the index path is out of bounds. - */ -extern id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) AS_WARN_UNUSED_RESULT; - - -ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Private/ASMultidimensionalArrayUtils.mm b/Source/Private/ASMultidimensionalArrayUtils.mm deleted file mode 100644 index bfa6c65eb4..0000000000 --- a/Source/Private/ASMultidimensionalArrayUtils.mm +++ /dev/null @@ -1,242 +0,0 @@ -// -// ASMultidimensionalArrayUtils.mm -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import -#import - -// Import UIKit to get [NSIndexPath indexPathForItem:inSection:] which uses -// tagged pointers. -#import - -#pragma mark - Internal Methods - -static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, - const NSArray *indexPaths, - NSUInteger &curIdx, - NSIndexPath *curIndexPath, - const NSUInteger dimension, - void (^updateBlock)(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx)) -{ - if (curIdx == indexPaths.count) { - return; - } - - if (curIndexPath.length < dimension - 1) { - NSInteger i = 0; - for (NSMutableArray *subarray in mutableArray) { - ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(subarray, indexPaths, curIdx, [curIndexPath indexPathByAddingIndex:i], dimension, updateBlock); - i += 1; - } - } else { - NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init]; - - while (curIdx < indexPaths.count && - [curIndexPath isEqual:[indexPaths[curIdx] indexPathByRemovingLastIndex]]) { - [indexSet addIndex:[indexPaths[curIdx] indexAtPosition:curIndexPath.length]]; - curIdx++; - } - - if (updateBlock){ - updateBlock(mutableArray, indexSet, curIdx); - } - } -} - -static void ASRecursivelyFindIndexPathsForMultidimensionalArray(NSObject *obj, NSIndexPath *curIndexPath, NSMutableArray *res) -{ - if (![obj isKindOfClass:[NSArray class]]) { - [res addObject:curIndexPath]; - } else { - NSArray *array = (NSArray *)obj; - NSUInteger idx = 0; - for (NSArray *subarray in array) { - ASRecursivelyFindIndexPathsForMultidimensionalArray(subarray, [curIndexPath indexPathByAddingIndex:idx], res); - idx++; - } - } -} - -static BOOL ASElementExistsAtIndexPathForMultidimensionalArray(NSArray *array, NSIndexPath *indexPath) { - NSUInteger indexLength = indexPath.length; - ASDisplayNodeCAssert(indexLength != 0, @"Must have a non-zero indexPath length"); - NSUInteger firstIndex = [indexPath indexAtPosition:0]; - BOOL elementExists = firstIndex < array.count; - - if (indexLength == 1) { - return elementExists; - } - - if (!elementExists) { - return NO; - } - - NSUInteger indexesLength = indexLength - 1; - NSUInteger indexes[indexesLength]; - [indexPath getIndexes:indexes range:NSMakeRange(1, indexesLength)]; - NSIndexPath *indexPathByRemovingFirstIndex; - // Use -indexPathForItem:inSection: if possible because it does not allocate into the heap - if (indexesLength == 2) { - indexPathByRemovingFirstIndex = [NSIndexPath indexPathForItem:indexes[1] inSection:indexes[0]]; - } else { - indexPathByRemovingFirstIndex = [NSIndexPath indexPathWithIndexes:indexes length:indexesLength]; - } - - return ASElementExistsAtIndexPathForMultidimensionalArray(array[firstIndex], indexPathByRemovingFirstIndex); -} - -#pragma mark - Public Methods - -NSObject *ASMultidimensionalArrayDeepMutableCopy(NSObject *obj) -{ - if ([obj isKindOfClass:[NSArray class]]) { - NSArray *arr = (NSArray *)obj; - NSMutableArray * mutableArr = [NSMutableArray arrayWithCapacity:arr.count]; - for (NSObject *elem in arr) { - [mutableArr addObject:ASMultidimensionalArrayDeepMutableCopy(elem)]; - } - return mutableArr; - } - - return obj; -} - -NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) -{ - NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; - for (NSArray *subarray in array) { - ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); - [newArray addObject:[subarray mutableCopy]]; - } - return newArray; -} - -void ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths, NSArray *elements) -{ - ASDisplayNodeCAssert(indexPaths.count == elements.count, @"Inconsistent indexPaths and elements"); - - if (!indexPaths.count) { - return; - } - - NSUInteger curIdx = 0; - NSIndexPath *indexPath = [[NSIndexPath alloc] init]; - ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(mutableArray, indexPaths, curIdx, indexPath, [indexPaths[0] length], ^(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx) { - [arr insertObjects:[elements subarrayWithRange:NSMakeRange(idx - indexSet.count, indexSet.count)] - atIndexes:indexSet]; - }); - - ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !"); -} - -void ASDeleteElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) -{ - if (!indexPaths.count) { - return; - } - - NSUInteger curIdx = 0; - NSIndexPath *indexPath = [[NSIndexPath alloc] init]; - - ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(mutableArray, indexPaths, curIdx, indexPath, [indexPaths[0] length], ^(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx) { - [arr removeObjectsAtIndexes:indexSet]; - }); - - ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !"); -} - -NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) -{ - NSUInteger curIdx = 0; - NSIndexPath *indexPath = [[NSIndexPath alloc] init]; - NSMutableArray *deletedElements = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; - - if (!indexPaths.count) { - return deletedElements; - } - - ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(mutableArray, indexPaths, curIdx, indexPath, [indexPaths[0] length], ^(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx) { - [deletedElements addObjectsFromArray:[arr objectsAtIndexes:indexSet]]; - }); - - ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !"); - - return deletedElements; -} - -NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensionalArray, NSIndexSet *indexSet) -{ - NSMutableArray *res = [NSMutableArray array]; - [indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { - for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { - ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray[i], [NSIndexPath indexPathWithIndex:i], res); - } - }]; - - return res; -} - -void ASMoveElementInTwoDimensionalArray(NSMutableArray *mutableArray, NSIndexPath *sourceIndexPath, NSIndexPath *destinationIndexPath) -{ - NSMutableArray *oldSection = mutableArray[sourceIndexPath.section]; - NSInteger oldItem = sourceIndexPath.item; - id object = oldSection[oldItem]; - [oldSection removeObjectAtIndex:oldItem]; - [mutableArray[destinationIndexPath.section] insertObject:object atIndex:destinationIndexPath.item]; -} - -NSArray *ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths(NSArray *multidimensionalArray, NSArray *indexPaths) -{ - NSMutableArray *res = [NSMutableArray array]; - for (NSIndexPath *indexPath in indexPaths) { - if (ASElementExistsAtIndexPathForMultidimensionalArray(multidimensionalArray, indexPath)) { - [res addObject:indexPath]; - } - } - - return res; -} - -NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) -{ - NSMutableArray *result = [NSMutableArray array]; - NSUInteger section = 0; - for (NSArray *subarray in twoDimensionalArray) { - ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); - for (NSUInteger item = 0; item < subarray.count; item++) { - [result addObject:[NSIndexPath indexPathForItem:item inSection:section]]; - } - section++; - } - return result; -} - -NSArray *ASIndexPathsForMultidimensionalArray(NSArray *multidimensionalArray) -{ - NSMutableArray *res = [NSMutableArray array]; - ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray, [[NSIndexPath alloc] init], res); - return res; -} - -id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) -{ - ASDisplayNodeCAssertNotNil(indexPath, @"Expected non-nil index path"); - ASDisplayNodeCAssert(indexPath.length == 2, @"Expected index path of length 2. Index path: %@", indexPath); - NSInteger section = indexPath.section; - if (array.count <= section) { - return nil; - } - - NSArray *innerArray = array[section]; - NSInteger item = indexPath.item; - if (innerArray.count <= item) { - return nil; - } - return innerArray[item]; -} diff --git a/Source/Private/ASMutableElementMap.m b/Source/Private/ASMutableElementMap.m index 40b90ca967..b818d531c0 100644 --- a/Source/Private/ASMutableElementMap.m +++ b/Source/Private/ASMutableElementMap.m @@ -11,7 +11,7 @@ #import #import #import -#import +#import #import typedef NSMutableArray *> ASMutableCollectionElementTwoDimensionalArray; @@ -51,8 +51,7 @@ typedef NSMutableDictionary *)indexPaths { - indexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; - ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_sectionsOfItems, indexPaths); + ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(_sectionsOfItems, indexPaths); } - (void)removeSectionContextsAtIndexes:(NSIndexSet *)indexes diff --git a/Source/Private/ASTwoDimensionalArrayUtils.h b/Source/Private/ASTwoDimensionalArrayUtils.h new file mode 100644 index 0000000000..c907227cc2 --- /dev/null +++ b/Source/Private/ASTwoDimensionalArrayUtils.h @@ -0,0 +1,48 @@ +// +// ASTwoDimensionalArrayUtils.h +// AsyncDisplayKit +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +#import + + +NS_ASSUME_NONNULL_BEGIN + +/** + * Helper class for operation on two-dimensional array, where the objects of the root array are each arrays + */ + +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * Deep mutable copy of an array that contains arrays, which contain objects. It will go one level deep into the array to copy. + * This method is substantially faster than the generalized version, e.g. about 10x faster, so use it whenever it fits the need. + */ +extern NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) AS_WARN_UNUSED_RESULT; + +/** + * Delete the elements of the mutable two-dimensional array at given index paths – sorted in descending order! + */ +extern void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths); + +/** + * Return all the index paths of a two-dimensional array, in ascending order. + */ +extern NSArray *ASIndexPathsForTwoDimensionalArray(NSArray* twoDimensionalArray) AS_WARN_UNUSED_RESULT; + +/** + * Attempt to get the object at the given index path. Returns @c nil if the index path is out of bounds. + */ +extern id _Nullable ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) AS_WARN_UNUSED_RESULT; + + +ASDISPLAYNODE_EXTERN_C_END + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASTwoDimensionalArrayUtils.m b/Source/Private/ASTwoDimensionalArrayUtils.m new file mode 100644 index 0000000000..c0d8888a3b --- /dev/null +++ b/Source/Private/ASTwoDimensionalArrayUtils.m @@ -0,0 +1,94 @@ +// +// ASTwoDimensionalArrayUtils.m +// AsyncDisplayKit +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import +#import +#import + +// Import UIKit to get [NSIndexPath indexPathForItem:inSection:] which uses +// tagged pointers. +#import + +#pragma mark - Public Methods + +NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) +{ + NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; + for (NSArray *subarray in array) { + ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); + [newArray addObject:[subarray mutableCopy]]; + } + return newArray; +} + +void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) +{ + if (indexPaths.count == 0) { + return; + } + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(asdk_inverseCompare:)]; + ASDisplayNodeCAssert([sortedIndexPaths isEqualToArray:indexPaths], @"Expected array of index paths to be sorted in descending order."); +#endif + + /** + * It is tempting to do something clever here and collect indexes into ranges or NSIndexSets + * but deep down, __NSArrayM only implements removeObjectAtIndex: and so doing all that extra + * work ends up running the same code. + */ + for (NSIndexPath *indexPath in indexPaths) { + NSInteger section = indexPath.section; + if (section >= mutableArray.count) { + ASDisplayNodeCFailAssert(@"Invalid section index %zd – only %zd sections", section, mutableArray.count); + continue; + } + + NSMutableArray *subarray = mutableArray[section]; + NSInteger item = indexPath.item; + if (item >= subarray.count) { + ASDisplayNodeCFailAssert(@"Invalid item index %zd – only %zd items in section %zd", item, subarray.count, section); + continue; + } + [subarray removeObjectAtIndex:item]; + } +} + +NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) +{ + NSMutableArray *result = [NSMutableArray array]; + NSInteger section = 0; + for (NSArray *subarray in twoDimensionalArray) { + ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); + NSInteger itemCount = subarray.count; + for (NSInteger item = 0; item < itemCount; item++) { + [result addObject:[NSIndexPath indexPathForItem:item inSection:section]]; + } + section++; + } + return result; +} + +id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) +{ + ASDisplayNodeCAssertNotNil(indexPath, @"Expected non-nil index path"); + ASDisplayNodeCAssert(indexPath.length == 2, @"Expected index path of length 2. Index path: %@", indexPath); + NSInteger section = indexPath.section; + if (array.count <= section) { + return nil; + } + + NSArray *innerArray = array[section]; + NSInteger item = indexPath.item; + if (innerArray.count <= item) { + return nil; + } + return innerArray[item]; +}