mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-10 08:20:16 +00:00
Faster collection operations (#748)
* Faster collection operations * Fix a few things * Put the stupid semicolon * Address warning * Cut down retain/releases during collection operations * Update CHANGELOG.md
This commit is contained in:
parent
b5d3e52e8b
commit
5c13403ef7
@ -60,6 +60,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
disableMainThreadChecker = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
- [ASCollectionNode] Added support for interactive item movement. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
- Added an experimental "no-copy" rendering API. See ASGraphicsContext.h for info. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
- Dropped support for iOS 8. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
- Optimize internal collection operations. [Adlai Holler](https://github.com/Adlai-Holler) [#748](https://github.com/TextureGroup/Texture/pull/748)
|
||||
|
||||
## 2.6
|
||||
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
|
||||
|
||||
@ -719,19 +719,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
- (NSArray<NSIndexPath *> *)convertIndexPathsToCollectionNode:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
if (indexPaths == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray<NSIndexPath *> *indexPathsArray = [NSMutableArray arrayWithCapacity:indexPaths.count];
|
||||
|
||||
for (NSIndexPath *indexPathInView in indexPaths) {
|
||||
NSIndexPath *indexPath = [self convertIndexPathToCollectionNode:indexPathInView];
|
||||
if (indexPath != nil) {
|
||||
[indexPathsArray addObject:indexPath];
|
||||
}
|
||||
}
|
||||
return indexPathsArray;
|
||||
return ASArrayByFlatMapping(indexPaths, NSIndexPath *indexPathInView, ({
|
||||
[self convertIndexPathToCollectionNode:indexPathInView];
|
||||
}));
|
||||
}
|
||||
|
||||
- (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
@ -747,17 +737,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
- (NSArray *)visibleNodes
|
||||
{
|
||||
NSArray *indexPaths = [self indexPathsForVisibleItems];
|
||||
NSMutableArray *visibleNodes = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSIndexPath *indexPath in indexPaths) {
|
||||
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
|
||||
if (node) {
|
||||
// It is possible for UICollectionView to return indexPaths before the node is completed.
|
||||
[visibleNodes addObject:node];
|
||||
}
|
||||
}
|
||||
|
||||
return visibleNodes;
|
||||
return ASArrayByFlatMapping(indexPaths, NSIndexPath *indexPath, ({
|
||||
[self nodeForItemAtIndexPath:indexPath];
|
||||
}));
|
||||
}
|
||||
|
||||
- (BOOL)usesSynchronousDataLoading
|
||||
@ -2176,13 +2159,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray<NSIndexPath *> *uikitIndexPaths = [NSMutableArray arrayWithCapacity:nodes.count];
|
||||
for (ASCellNode *node in nodes) {
|
||||
NSIndexPath *uikitIndexPath = [self indexPathForNode:node];
|
||||
if (uikitIndexPath != nil) {
|
||||
[uikitIndexPaths addObject:uikitIndexPath];
|
||||
}
|
||||
}
|
||||
NSArray *uikitIndexPaths = ASArrayByFlatMapping(nodes, ASCellNode *node, ({
|
||||
[self indexPathForNode:node];
|
||||
}));
|
||||
|
||||
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:uikitIndexPaths batched:NO];
|
||||
|
||||
|
||||
@ -158,14 +158,12 @@
|
||||
- (void)setupYogaCalculatedLayout
|
||||
{
|
||||
YGNodeRef yogaNode = self.style.yogaNode;
|
||||
uint32_t childCount = YGNodeGetChildCount(yogaNode);
|
||||
ASDisplayNodeAssert(childCount == self.yogaChildren.count,
|
||||
ASDisplayNodeAssert(YGNodeGetChildCount(yogaNode) == self.yogaChildren.count,
|
||||
@"Yoga tree should always be in sync with .yogaNodes array! %@", self.yogaChildren);
|
||||
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:childCount];
|
||||
for (ASDisplayNode *subnode in self.yogaChildren) {
|
||||
[sublayouts addObject:[subnode layoutForYogaNode]];
|
||||
}
|
||||
NSArray *sublayouts = ASArrayByFlatMapping(self.yogaChildren, ASDisplayNode *subnode, ({
|
||||
[subnode layoutForYogaNode];
|
||||
}));
|
||||
|
||||
// The layout for self should have position CGPointNull, but include the calculated size.
|
||||
CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode));
|
||||
|
||||
@ -607,15 +607,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray<NSIndexPath *> *indexPathsArray = [NSMutableArray new];
|
||||
|
||||
for (NSIndexPath *indexPathInView in indexPaths) {
|
||||
NSIndexPath *indexPath = [self convertIndexPathToTableNode:indexPathInView];
|
||||
if (indexPath != nil) {
|
||||
[indexPathsArray addObject:indexPath];
|
||||
}
|
||||
}
|
||||
return indexPathsArray;
|
||||
return ASArrayByFlatMapping(indexPaths, NSIndexPath *indexPathInView, ({
|
||||
[self convertIndexPathToTableNode:indexPathInView];
|
||||
}));
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
|
||||
@ -132,22 +132,6 @@
|
||||
#define __has_attribute(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#ifndef NS_CONSUMED
|
||||
#if __has_feature(attribute_ns_consumed)
|
||||
#define NS_CONSUMED __attribute__((ns_consumed))
|
||||
#else
|
||||
#define NS_CONSUMED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_retained)
|
||||
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
|
||||
#else
|
||||
#define NS_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_retained)
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
@ -245,26 +229,38 @@
|
||||
* Create a new set by mapping `collection` over `work`, ignoring nil.
|
||||
*/
|
||||
#define ASSetByFlatMapping(collection, decl, work) ({ \
|
||||
NSMutableSet *s = [NSMutableSet set]; \
|
||||
CFTypeRef _cArray[collection.count]; \
|
||||
NSUInteger _i = 0; \
|
||||
for (decl in collection) {\
|
||||
id result = work; \
|
||||
if (result != nil) { \
|
||||
[s addObject:result]; \
|
||||
if ((_cArray[_i] = (__bridge_retained CFTypeRef)work)) { \
|
||||
_i++; \
|
||||
} \
|
||||
} \
|
||||
s; \
|
||||
NSSet *result; \
|
||||
if (_i == 0) { \
|
||||
/** Zero fast path. */ \
|
||||
result = [NSSet set]; \
|
||||
} else if (_i == 1) { \
|
||||
/** NSSingleObjectSet is fast. Create one and release. */ \
|
||||
CFTypeRef val = _cArray[0]; \
|
||||
result = [NSSet setWithObject:(__bridge id)val]; \
|
||||
CFBridgingRelease(val); \
|
||||
} else { \
|
||||
CFSetCallBacks cb = kCFTypeSetCallBacks; \
|
||||
cb.retain = NULL; \
|
||||
result = (__bridge NSSet *)CFSetCreate(kCFAllocatorDefault, _cArray, _i, &cb); \
|
||||
} \
|
||||
result; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Create a new ObjectPointerPersonality NSHashTable by mapping `collection` over `work`, ignoring nil.
|
||||
*/
|
||||
#define ASPointerTableByFlatMapping(collection, decl, work) ({ \
|
||||
NSHashTable *t = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; \
|
||||
NSHashTable *t = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:collection.count]; \
|
||||
for (decl in collection) {\
|
||||
id result = work; \
|
||||
if (result != nil) { \
|
||||
[t addObject:result]; \
|
||||
} \
|
||||
/* NSHashTable accepts nil and avoid extra retain/release. */ \
|
||||
[t addObject:work]; \
|
||||
} \
|
||||
t; \
|
||||
})
|
||||
@ -273,12 +269,37 @@
|
||||
* Create a new array by mapping `collection` over `work`, ignoring nil.
|
||||
*/
|
||||
#define ASArrayByFlatMapping(collection, decl, work) ({ \
|
||||
NSMutableArray *a = [NSMutableArray array]; \
|
||||
CFTypeRef _cArray[collection.count]; \
|
||||
NSUInteger _i = 0; \
|
||||
for (decl in collection) {\
|
||||
id result = work; \
|
||||
if (result != nil) { \
|
||||
[a addObject:result]; \
|
||||
if ((_cArray[_i] = (__bridge_retained CFTypeRef)work)) { \
|
||||
_i++; \
|
||||
} \
|
||||
} \
|
||||
a; \
|
||||
NSArray *result; \
|
||||
if (_i == 0) { \
|
||||
/** Zero array fast path. */ \
|
||||
result = @[]; \
|
||||
} else if (_i == 1) { \
|
||||
/** NSSingleObjectArray is fast. Create one and release. */ \
|
||||
CFTypeRef val = _cArray[0]; \
|
||||
result = [NSArray arrayWithObject:(__bridge id)val]; \
|
||||
CFBridgingRelease(val); \
|
||||
} else { \
|
||||
CFArrayCallBacks cb = kCFTypeArrayCallBacks; \
|
||||
cb.retain = NULL; \
|
||||
result = (__bridge NSArray *)CFArrayCreate(kCFAllocatorDefault, _cArray, _i, &cb); \
|
||||
} \
|
||||
result; \
|
||||
})
|
||||
|
||||
#define ASMutableArrayByFlatMapping(collection, decl, work) ({ \
|
||||
id _cArray[collection.count]; \
|
||||
NSUInteger _i = 0; \
|
||||
for (decl in collection) {\
|
||||
if ((_cArray[_i] = work)) { \
|
||||
_i++; \
|
||||
} \
|
||||
} \
|
||||
[[NSMutableArray alloc] initWithObjects:_cArray count:_i]; \
|
||||
})
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
+ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context
|
||||
{
|
||||
ASElementMap *elements = context.elements;
|
||||
NSMutableArray<ASCellNode *> *children = ASArrayByFlatMapping(elements.itemElements, ASCollectionElement *element, element.node);
|
||||
NSArray<ASCellNode *> *children = ASArrayByFlatMapping(elements.itemElements, ASCollectionElement *element, element.node);
|
||||
if (children.count == 0) {
|
||||
return [[ASCollectionLayoutState alloc] initWithContext:context];
|
||||
}
|
||||
|
||||
@ -102,9 +102,9 @@
|
||||
return [[ASCollectionLayoutState alloc] initWithContext:context];
|
||||
}
|
||||
|
||||
NSMutableArray<_ASGalleryLayoutItem *> *children = ASArrayByFlatMapping(elements.itemElements,
|
||||
ASCollectionElement *element,
|
||||
[[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]);
|
||||
NSArray<_ASGalleryLayoutItem *> *children = ASArrayByFlatMapping(elements.itemElements,
|
||||
ASCollectionElement *element,
|
||||
[[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]);
|
||||
if (children.count == 0) {
|
||||
return [[ASCollectionLayoutState alloc] initWithContext:context];
|
||||
}
|
||||
|
||||
@ -31,6 +31,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
AS_SUBCLASSING_RESTRICTED
|
||||
@interface ASElementMap : NSObject <NSCopying, NSFastEnumeration>
|
||||
|
||||
/**
|
||||
* The total number of elements in this map.
|
||||
*/
|
||||
@property (readonly) NSUInteger count;
|
||||
|
||||
/**
|
||||
* The number of sections (of items) in this map.
|
||||
*/
|
||||
|
||||
@ -75,6 +75,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)count
|
||||
{
|
||||
return _elementToIndexPathMap.count;
|
||||
}
|
||||
|
||||
- (NSArray<NSIndexPath *> *)itemIndexPaths
|
||||
{
|
||||
return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems);
|
||||
|
||||
@ -203,13 +203,9 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
|
||||
_sublayoutLayoutElements = nil;
|
||||
} else {
|
||||
// Add sublayouts layout elements to an internal array to retain it while the layout lives
|
||||
NSUInteger sublayoutCount = _sublayouts.count;
|
||||
if (sublayoutCount > 0) {
|
||||
_sublayoutLayoutElements = [NSMutableArray arrayWithCapacity:sublayoutCount];
|
||||
for (ASLayout *sublayout in _sublayouts) {
|
||||
[_sublayoutLayoutElements addObject:sublayout.layoutElement];
|
||||
}
|
||||
}
|
||||
_sublayoutLayoutElements = ASMutableArrayByFlatMapping(_sublayouts, ASLayout *sublayout, ({
|
||||
sublayout.layoutElement;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +232,7 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
|
||||
queue.push_back({sublayout, sublayout.position});
|
||||
}
|
||||
|
||||
NSMutableArray *flattenedSublayouts = [NSMutableArray array];
|
||||
std::vector<ASLayout *> flattenedSublayouts;
|
||||
|
||||
while (!queue.empty()) {
|
||||
const Context context = queue.front();
|
||||
@ -255,7 +251,7 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
|
||||
position:absolutePosition
|
||||
sublayouts:@[]];
|
||||
}
|
||||
[flattenedSublayouts addObject:layout];
|
||||
flattenedSublayouts.push_back(layout);
|
||||
} else if (sublayoutsCount > 0){
|
||||
std::vector<Context> sublayoutContexts;
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
@ -265,7 +261,8 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
|
||||
}
|
||||
}
|
||||
|
||||
ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size sublayouts:flattenedSublayouts];
|
||||
NSArray *sublayoutsArray = [NSArray arrayWithObjects:flattenedSublayouts.data() count:flattenedSublayouts.size()];
|
||||
ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size sublayouts:sublayoutsArray];
|
||||
// All flattened layouts must have this flag enabled
|
||||
// to ensure sublayout elements are retained until the layouts are applied.
|
||||
layout.retainSublayoutLayoutElements = YES;
|
||||
|
||||
@ -295,19 +295,15 @@ ASLayoutElementStyleExtensibilityForwarding
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
NSArray *children = self.children;
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
|
||||
|
||||
CGSize size = constrainedSize.min;
|
||||
for (id<ASLayoutElement> child in children) {
|
||||
NSArray *sublayouts = ASArrayByFlatMapping(self.children, id<ASLayoutElement> child, ({
|
||||
ASLayout *sublayout = [child layoutThatFits:constrainedSize parentSize:constrainedSize.max];
|
||||
sublayout.position = CGPointZero;
|
||||
|
||||
size.width = MAX(size.width, sublayout.size.width);
|
||||
size.height = MAX(size.height, sublayout.size.height);
|
||||
|
||||
[sublayouts addObject:sublayout];
|
||||
}
|
||||
sublayout;
|
||||
}));
|
||||
|
||||
return [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
@ -27,13 +27,10 @@
|
||||
|
||||
NSMutableArray<NSMutableArray *> *ASTwoDimensionalArrayDeepMutableCopy(NSArray<NSArray *> *array)
|
||||
{
|
||||
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
|
||||
NSInteger i = 0;
|
||||
for (NSArray *subarray in array) {
|
||||
return ASMutableArrayByFlatMapping(array, NSArray *subarray, ({
|
||||
ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray<NSArray *> *");
|
||||
newArray[i++] = [subarray mutableCopy];
|
||||
}
|
||||
return newArray;
|
||||
[subarray mutableCopy];
|
||||
}));
|
||||
}
|
||||
|
||||
void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray<NSIndexPath *> *indexPaths)
|
||||
|
||||
@ -69,11 +69,9 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi
|
||||
|
||||
- (NSArray<ASDisplayNode *> *)subnodesForKey:(NSString *)key
|
||||
{
|
||||
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray array];
|
||||
for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) {
|
||||
[subnodes addObject:(ASDisplayNode *)sublayout.layoutElement];
|
||||
}
|
||||
return subnodes;
|
||||
return ASArrayByFlatMapping([self layoutForKey:key].sublayouts, ASLayout *sublayout, ({
|
||||
(ASDisplayNode *)sublayout.layoutElement;
|
||||
}));
|
||||
}
|
||||
|
||||
- (NSArray<ASDisplayNode *> *)insertedSubnodes
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user