Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit c90ed08

Browse files
maickiAdlai Holler
authored andcommitted
[ASLayoutSpec] Use childrenMap directly to prevent creating an NSArray within ASDK (#1937)
* Use childrenMap directly to prevent creating an NSArray in ASDK for ASLayoutSpec children * Add locking for parent property in ASLayoutSpec * Remove unnecessary import * Add newline * Add NSFastEnumeration to ASEnvironment and ASDisplayNode / ASLayoutSpec * Change NSMutableArray initializer to arrayWithCapacity: * Move ASLayoutSpec+Private.h into Private folder Fixes building with Swift * Remove lock for ASLayoutSpec parent
1 parent 678df37 commit c90ed08

9 files changed

Lines changed: 135 additions & 45 deletions

File tree

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@
214214
69E1006F1CA89CB600D88C1B /* ASEnvironmentInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */; };
215215
69E100701CA89CB600D88C1B /* ASEnvironmentInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */; };
216216
69F10C871C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 69F10C851C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
217+
69F6058D1D3DA27E00C8CA38 /* ASLayoutSpec+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 69F6058C1D3DA27E00C8CA38 /* ASLayoutSpec+Private.h */; };
217218
7630FFA81C9E267E007A7C0E /* ASVideoNode.h in Headers */ = {isa = PBXBuildFile; fileRef = AEEC47DF1C20C2DD00EC1693 /* ASVideoNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
218219
764D83D51C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */; settings = {ATTRIBUTES = (Public, ); }; };
219220
764D83D61C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m in Sources */ = {isa = PBXBuildFile; fileRef = 764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */; };
@@ -977,6 +978,7 @@
977978
69E100691CA89CB600D88C1B /* ASEnvironmentInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEnvironmentInternal.h; sourceTree = "<group>"; };
978979
69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASEnvironmentInternal.mm; sourceTree = "<group>"; };
979980
69F10C851C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASRangeControllerUpdateRangeProtocol+Beta.h"; sourceTree = "<group>"; };
981+
69F6058C1D3DA27E00C8CA38 /* ASLayoutSpec+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASLayoutSpec+Private.h"; sourceTree = "<group>"; };
980982
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = "<group>"; };
981983
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+Debug.h"; sourceTree = "<group>"; };
982984
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+Debug.m"; sourceTree = "<group>"; };
@@ -1482,6 +1484,8 @@
14821484
044285051BAA63FE00D16268 /* ASBatchFetching.h */,
14831485
044285061BAA63FE00D16268 /* ASBatchFetching.m */,
14841486
251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */,
1487+
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
1488+
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
14851489
AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */,
14861490
AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */,
14871491
058D0A08195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm */,
@@ -1490,16 +1494,17 @@
14901494
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */,
14911495
058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */,
14921496
058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */,
1493-
E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */,
1494-
E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */,
14951497
69E100691CA89CB600D88C1B /* ASEnvironmentInternal.h */,
14961498
69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */,
1499+
68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */,
14971500
058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */,
14981501
058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */,
1499-
68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */,
15001502
ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */,
1501-
ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.m */,
1503+
ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.mm */,
1504+
69F6058C1D3DA27E00C8CA38 /* ASLayoutSpec+Private.h */,
15021505
ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */,
1506+
E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */,
1507+
E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */,
15031508
0442850B1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h */,
15041509
0442850C1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm */,
15051510
CC3B20811C3F76D600798563 /* ASPendingStateController.h */,
@@ -1517,8 +1522,6 @@
15171522
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
15181523
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
15191524
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.m */,
1520-
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
1521-
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
15221525
);
15231526
path = Private;
15241527
sourceTree = "<group>";
@@ -1752,6 +1755,7 @@
17521755
B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */,
17531756
680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */,
17541757
B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */,
1758+
69F6058D1D3DA27E00C8CA38 /* ASLayoutSpec+Private.h in Headers */,
17551759
B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */,
17561760
C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */,
17571761
AC7A2C181BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */,

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,15 @@ - (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)environmentT
29542954
}
29552955
}
29562956

2957+
#pragma mark - NSFastEnumeration
2958+
2959+
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
2960+
objects:(id __unsafe_unretained [])stackbuf
2961+
count:(NSUInteger)stackbufLength
2962+
{
2963+
return [self.children countByEnumeratingWithState:state objects:stackbuf count:stackbufLength];
2964+
}
2965+
29572966
ASEnvironmentLayoutOptionsForwarding
29582967
ASEnvironmentLayoutExtensibilityForwarding
29592968

AsyncDisplayKit/ASViewController.mm

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,7 @@ - (void)progagateNewEnvironmentTraitCollection:(ASEnvironmentTraitCollection)env
289289
self.node.environmentState = environmentState;
290290
[self.node setNeedsLayout];
291291

292-
NSArray<id<ASEnvironment>> *children = [self.node children];
293-
for (id<ASEnvironment> child in children) {
292+
for (id<ASEnvironment> child in self.node) {
294293
ASEnvironmentStatePropagateDown(child, environmentState.environmentTraitCollection);
295294
}
296295
}

AsyncDisplayKit/Details/ASEnvironment.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ ASDISPLAYNODE_EXTERN_C_END
9999
* defined in an ASEnvironmentState up and down the ASEnvironment tree. To be able to define how merges of
100100
* States should happen, specific merge functions can be provided
101101
*/
102-
@protocol ASEnvironment <NSObject>
102+
@protocol ASEnvironment <NSObject, NSFastEnumeration>
103103

104104
/// The environment collection of an object which class conforms to the ASEnvironment protocol
105105
- (ASEnvironmentState)environmentState;
@@ -126,6 +126,7 @@ ASDISPLAYNODE_EXTERN_C_END
126126

127127
/// sets a trait collection on this environment state.
128128
- (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection;
129+
129130
@end
130131

131132
// ASCollection/TableNodes don't actually have ASCellNodes as subnodes. Because of this we can't rely on display trait

AsyncDisplayKit/Layout/ASLayoutSpec.mm

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// of patent rights can be found in the PATENTS file in the same directory.
99
//
1010

11-
#import "ASLayoutSpec.h"
11+
#import "ASLayoutSpec+Private.h"
1212

1313
#import "ASAssert.h"
1414
#import "ASEnvironmentInternal.h"
@@ -17,16 +17,12 @@
1717
#import "ASThread.h"
1818
#import "ASTraitCollection.h"
1919

20-
#import <objc/runtime.h>
21-
#import <map>
2220
#import <vector>
2321

24-
typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASChildMap;
25-
2622
@interface ASLayoutSpec() {
2723
ASEnvironmentState _environmentState;
2824
ASDN::RecursiveMutex __instanceLock__;
29-
ASChildMap _children;
25+
ASChildrenMap _childrenMap;
3026
}
3127
@end
3228

@@ -35,6 +31,7 @@ @implementation ASLayoutSpec
3531
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
3632
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis,
3733
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType;
34+
@synthesize parent = _parent;
3835
@synthesize isFinalLayoutable = _isFinalLayoutable;
3936

4037
- (instancetype)init
@@ -105,27 +102,35 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
105102
return child;
106103
}
107104

105+
#pragma mark - Parent
106+
108107
- (void)setParent:(id<ASLayoutable>)parent
109108
{
110-
// FIXME: Locking should be evaluated here. _parent is not widely used yet, though.
111109
_parent = parent;
112110

113111
if ([parent supportsUpwardPropagation]) {
114112
ASEnvironmentStatePropagateUp(parent, self.environmentState.layoutOptionsState);
115113
}
116114
}
117115

116+
- (id<ASLayoutable>)parent
117+
{
118+
return _parent;
119+
}
120+
121+
#pragma mark - Children
122+
118123
- (void)setChild:(id<ASLayoutable>)child
119124
{
120125
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
121126
if (child) {
122127
id<ASLayoutable> finalLayoutable = [self layoutableToAddFromLayoutable:child];
123128
if (finalLayoutable) {
124-
_children[0] = finalLayoutable;
129+
_childrenMap[0] = finalLayoutable;
125130
[self propagateUpLayoutable:finalLayoutable];
126131
}
127132
} else {
128-
_children.erase(0);
133+
_childrenMap.erase(0);
129134
}
130135
}
131136

@@ -134,9 +139,9 @@ - (void)setChild:(id<ASLayoutable>)child forIndex:(NSUInteger)index
134139
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
135140
if (child) {
136141
id<ASLayoutable> finalLayoutable = [self layoutableToAddFromLayoutable:child];
137-
_children[index] = finalLayoutable;
142+
_childrenMap[index] = finalLayoutable;
138143
} else {
139-
_children.erase(index);
144+
_childrenMap.erase(index);
140145
}
141146
// TODO: Should we propagate up the layoutable at it could happen that multiple children will propagated up their
142147
// layout options and one child will overwrite values from another child
@@ -147,37 +152,69 @@ - (void)setChildren:(NSArray<id<ASLayoutable>> *)children
147152
{
148153
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
149154

150-
_children.clear();
155+
_childrenMap.clear();
151156
NSUInteger i = 0;
152157
for (id<ASLayoutable> child in children) {
153-
_children[i] = [self layoutableToAddFromLayoutable:child];
158+
_childrenMap[i] = [self layoutableToAddFromLayoutable:child];
154159
i += 1;
155160
}
156161
}
157162

158163
- (id<ASLayoutable>)childForIndex:(NSUInteger)index
159164
{
160-
if (index < _children.size()) {
161-
return _children[index];
165+
if (index < _childrenMap.size()) {
166+
return _childrenMap[index];
162167
}
163168
return nil;
164169
}
165170

166171
- (id<ASLayoutable>)child
167172
{
168-
return _children[0];
173+
return _childrenMap[0];
169174
}
170175

171176
- (NSArray *)children
172177
{
178+
// If used inside ASDK, the childrenMap property should be preferred over the children array to prevent unecessary
179+
// boxing
173180
std::vector<ASLayout *> children;
174-
for (ASChildMap::iterator it = _children.begin(); it != _children.end(); ++it ) {
175-
children.push_back(it->second);
181+
for (auto const &entry : _childrenMap) {
182+
children.push_back(entry.second);
176183
}
177-
184+
178185
return [NSArray arrayWithObjects:&children[0] count:children.size()];
179186
}
180187

188+
#pragma mark - NSFastEnumeration
189+
190+
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
191+
objects:(id __unsafe_unretained [])stackbuf
192+
count:(NSUInteger)stackbufLength
193+
{
194+
NSUInteger count = 0;
195+
unsigned long countOfItemsAlreadyEnumerated = state->state;
196+
197+
if (countOfItemsAlreadyEnumerated == 0) {
198+
state->mutationsPtr = &state->extra[0];
199+
}
200+
201+
if (countOfItemsAlreadyEnumerated < _childrenMap.size()) {
202+
state->itemsPtr = stackbuf;
203+
204+
while((countOfItemsAlreadyEnumerated < _childrenMap.size()) && (count < stackbufLength)) {
205+
stackbuf[count] = _childrenMap[countOfItemsAlreadyEnumerated];
206+
countOfItemsAlreadyEnumerated++;
207+
count++;
208+
}
209+
} else {
210+
count = 0;
211+
}
212+
213+
state->state = countOfItemsAlreadyEnumerated;
214+
215+
return count;
216+
}
217+
181218
#pragma mark - ASEnvironment
182219

183220
- (ASEnvironmentState)environmentState
@@ -233,6 +270,15 @@ - (ASTraitCollection *)asyncTraitCollection
233270

234271
@end
235272

273+
@implementation ASLayoutSpec (Private)
274+
275+
- (ASChildrenMap)childrenMap
276+
{
277+
return _childrenMap;
278+
}
279+
280+
@end
281+
236282
@implementation ASLayoutSpec (Debugging)
237283

238284
#pragma mark - ASLayoutableAsciiArtProtocol

AsyncDisplayKit/Layout/ASStackLayoutSpec.mm

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#import "ASInternalHelpers.h"
1515

16+
#import "ASLayoutSpec+Private.h"
1617
#import "ASLayoutSpecUtilities.h"
1718
#import "ASStackBaselinePositionedLayout.h"
1819
#import "ASThread.h"
@@ -58,7 +59,7 @@ - (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGF
5859
_alignItems = alignItems;
5960
_justifyContent = justifyContent;
6061

61-
[self setChildren:children];
62+
self.children = children;
6263
return self;
6364
}
6465

@@ -120,7 +121,9 @@ - (void)setBaselineRelativeArrangement:(BOOL)baselineRelativeArrangement
120121

121122
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
122123
{
123-
if (self.children.count == 0) {
124+
auto const &children = self.childrenMap;
125+
126+
if (children.size() == 0) {
124127
return [ASLayout layoutWithLayoutableObject:self
125128
constrainedSizeRange:constrainedSize
126129
size:constrainedSize.min];
@@ -129,9 +132,9 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
129132
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement};
130133
BOOL needsBaselinePass = _baselineRelativeArrangement || _alignItems == ASStackLayoutAlignItemsBaselineFirst || _alignItems == ASStackLayoutAlignItemsBaselineLast;
131134

132-
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
133-
for (id<ASLayoutable> child in self.children) {
134-
stackChildren.push_back(child);
135+
std::vector<id<ASLayoutable>> stackChildren;
136+
for (auto const &entry : children) {
137+
stackChildren.push_back(entry.second);
135138
}
136139

137140
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
@@ -143,14 +146,18 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
143146
// regardless of whether or not this stack aligns to baseline, we should let ASStackBaselinePositionedLayout::compute find the max ascender
144147
// and min descender in case this spec is a child in another spec that wants to align to a baseline.
145148
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
146-
if (self.direction == ASStackLayoutDirectionVertical) {
147-
ASDN::MutexLocker l(__instanceLock__);
148-
self.ascender = [[self.children firstObject] ascender];
149-
self.descender = [[self.children lastObject] descender];
150-
} else {
149+
{
151150
ASDN::MutexLocker l(__instanceLock__);
152-
self.ascender = baselinePositionedLayout.ascender;
153-
self.descender = baselinePositionedLayout.descender;
151+
if (self.direction == ASStackLayoutDirectionVertical) {
152+
if (stackChildren.size() > 0) {
153+
self.ascender = stackChildren.front().ascender;
154+
self.descender = stackChildren.back().descender;
155+
}
156+
157+
} else {
158+
self.ascender = baselinePositionedLayout.ascender;
159+
self.descender = baselinePositionedLayout.descender;
160+
}
154161
}
155162

156163
if (needsBaselinePass) {

AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#import "ASStaticLayoutSpec.h"
1212

13+
#import "ASLayoutSpec+Private.h"
1314
#import "ASLayoutSpecUtilities.h"
1415
#import "ASLayout.h"
1516

@@ -38,10 +39,8 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
3839
{
3940
CGSize maxConstrainedSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height);
4041

41-
NSArray *children = self.children;
42-
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
43-
44-
for (id<ASLayoutable> child in children) {
42+
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.childrenMap.size()];
43+
for (id<ASLayoutable> child in self) {
4544
CGPoint layoutPosition = child.layoutPosition;
4645
CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x,
4746
maxConstrainedSize.height - layoutPosition.y);

AsyncDisplayKit/Private/ASEnvironmentInternal.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void ASEnvironmentPerformBlockOnObjectAndChildren(id<ASEnvironment> object, void
4444

4545
block(object);
4646

47-
for (id<ASEnvironment> child in [object children]) {
47+
for (id<ASEnvironment> child in object) {
4848
queue.push(child);
4949
}
5050
}

0 commit comments

Comments
 (0)