Reduce boxing overhead of arrays in uiBlockWithLayoutUpdateForRootView
Summary: The view update cycle in UIManager was relying on a bunch of boolean values boxes as NSNumbers in parallel arrays. This diff packs those values into a struct, which is more efficient and easier to maintain. Reviewed By: javache Differential Revision: D3365346 fbshipit-source-id: d9cbf2865421f76772c1761b13992d40ec3675f0
This commit is contained in:
parent
0b6764d18e
commit
5136d95f2c
|
@ -1014,12 +1014,6 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp;
|
||||
PRODUCT_NAME = UIExplorer;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wextra",
|
||||
"-Wall",
|
||||
"-Wincompatible-pointer-types",
|
||||
"-Wincompatible-pointer-types-discards-qualifiers",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -1039,12 +1033,6 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp;
|
||||
PRODUCT_NAME = UIExplorer;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wextra",
|
||||
"-Wall",
|
||||
"-Wincompatible-pointer-types",
|
||||
"-Wincompatible-pointer-types-discards-qualifiers",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -1149,6 +1137,8 @@
|
|||
"-Wextra",
|
||||
"-Wall",
|
||||
"-Wincompatible-pointer-types",
|
||||
"-Wincompatible-pointer-types-discards-qualifiers",
|
||||
"-Wshadow",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -1212,6 +1202,8 @@
|
|||
"-Wextra",
|
||||
"-Wall",
|
||||
"-Wincompatible-pointer-types",
|
||||
"-Wincompatible-pointer-types-discards-qualifiers",
|
||||
"-Wshadow",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -57,8 +57,6 @@ NSString *const RCTUIManagerRootViewKey = @"RCTUIManagerRootViewKey";
|
|||
@property (nonatomic, readonly) NSTimeInterval duration;
|
||||
@property (nonatomic, readonly) NSTimeInterval delay;
|
||||
@property (nonatomic, readonly, copy) NSString *property;
|
||||
@property (nonatomic, readonly) id fromValue;
|
||||
@property (nonatomic, readonly) id toValue;
|
||||
@property (nonatomic, readonly) CGFloat springDamping;
|
||||
@property (nonatomic, readonly) CGFloat initialVelocity;
|
||||
@property (nonatomic, readonly) RCTAnimationType animationType;
|
||||
|
@ -136,8 +134,6 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
|
|||
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
|
||||
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
|
||||
}
|
||||
_fromValue = config[@"fromValue"];
|
||||
_toValue = config[@"toValue"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -552,42 +548,47 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
|||
return nil;
|
||||
}
|
||||
|
||||
// Parallel arrays are built and then handed off to main thread
|
||||
NSMutableArray<NSNumber *> *frameReactTags =
|
||||
[NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
NSMutableArray<NSValue *> *frames =
|
||||
[NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
NSMutableArray<NSNumber *> *areNew =
|
||||
[NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
NSMutableArray<NSNumber *> *parentsAreNew =
|
||||
[NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
NSMutableDictionary<NSNumber *, RCTViewManagerUIBlock> *updateBlocks =
|
||||
[NSMutableDictionary new];
|
||||
NSMutableArray<NSNumber *> *areHidden =
|
||||
[NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
typedef struct {
|
||||
CGRect frame;
|
||||
BOOL isNew;
|
||||
BOOL parentIsNew;
|
||||
BOOL isHidden;
|
||||
} RCTFrameData;
|
||||
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
[frameReactTags addObject:shadowView.reactTag];
|
||||
[frames addObject:[NSValue valueWithCGRect:shadowView.frame]];
|
||||
[areNew addObject:@(shadowView.isNewView)];
|
||||
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
||||
[areHidden addObject:@(shadowView.isHidden)];
|
||||
}
|
||||
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
// We have to do this after we build the parentsAreNew array.
|
||||
shadowView.newView = NO;
|
||||
// Construct arrays then hand off to main thread
|
||||
NSInteger count = viewsWithNewFrames.count;
|
||||
NSMutableArray *reactTags = [[NSMutableArray alloc] initWithCapacity:count];
|
||||
NSMutableData *framesData = [[NSMutableData alloc] initWithLength:sizeof(RCTFrameData) * count];
|
||||
{
|
||||
NSInteger index = 0;
|
||||
RCTFrameData *frameDataArray = (RCTFrameData *)framesData.mutableBytes;
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
reactTags[index] = shadowView.reactTag;
|
||||
frameDataArray[index++] = (RCTFrameData){
|
||||
shadowView.frame,
|
||||
shadowView.isNewView,
|
||||
shadowView.superview.isNewView,
|
||||
shadowView.isHidden,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// These are blocks to be executed on each view, immediately after
|
||||
// reactSetFrame: has been called. Note that if reactSetFrame: is not called,
|
||||
// these won't be called either, so this is not a suitable place to update
|
||||
// properties that aren't related to layout.
|
||||
NSMutableDictionary<NSNumber *, RCTViewManagerUIBlock> *updateBlocks =
|
||||
[NSMutableDictionary new];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
|
||||
// We have to do this after we build the parentsAreNew array.
|
||||
shadowView.newView = NO;
|
||||
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager];
|
||||
RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView];
|
||||
if (block) {
|
||||
updateBlocks[shadowView.reactTag] = block;
|
||||
updateBlocks[reactTag] = block;
|
||||
}
|
||||
|
||||
if (shadowView.onLayout) {
|
||||
|
@ -602,8 +603,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
|||
});
|
||||
}
|
||||
|
||||
if (RCTIsReactRootView(shadowView.reactTag)) {
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
if (RCTIsReactRootView(reactTag)) {
|
||||
CGSize contentSize = shadowView.frame.size;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
@ -618,23 +618,28 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
|||
|
||||
// Perform layout (possibly animated)
|
||||
return ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
RCTLayoutAnimation *layoutAnimation = _layoutAnimation;
|
||||
|
||||
const RCTFrameData *frameDataArray = (const RCTFrameData *)framesData.bytes;
|
||||
RCTLayoutAnimation *layoutAnimation = uiManager->_layoutAnimation;
|
||||
|
||||
__block NSUInteger completionsCalled = 0;
|
||||
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
||||
NSNumber *reactTag = frameReactTags[ii];
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
CGRect frame = [frames[ii] CGRectValue];
|
||||
|
||||
BOOL isHidden = [areHidden[ii] boolValue];
|
||||
BOOL isNew = [areNew[ii] boolValue];
|
||||
NSInteger index = 0;
|
||||
for (NSNumber *reactTag in reactTags) {
|
||||
RCTFrameData frameData = frameDataArray[index++];
|
||||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
CGRect frame = frameData.frame;
|
||||
|
||||
BOOL isHidden = frameData.isHidden;
|
||||
BOOL isNew = frameData.isNew;
|
||||
RCTAnimation *updateAnimation = isNew ? nil : layoutAnimation.updateAnimation;
|
||||
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
|
||||
BOOL shouldAnimateCreation = isNew && !frameData.parentIsNew;
|
||||
RCTAnimation *createAnimation = shouldAnimateCreation ? layoutAnimation.createAnimation : nil;
|
||||
|
||||
void (^completion)(BOOL) = ^(BOOL finished) {
|
||||
completionsCalled++;
|
||||
if (layoutAnimation.callback && completionsCalled == frames.count) {
|
||||
if (layoutAnimation.callback && completionsCalled == count) {
|
||||
layoutAnimation.callback(@[@(finished)]);
|
||||
|
||||
// It's unsafe to call this callback more than once, so we nil it out here
|
||||
|
@ -647,58 +652,59 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
|||
view.hidden = isHidden;
|
||||
}
|
||||
|
||||
// Animate view creation
|
||||
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
|
||||
if (createAnimation) {
|
||||
|
||||
// Animate view creation
|
||||
[view reactSetFrame:frame];
|
||||
|
||||
CATransform3D finalTransform = view.layer.transform;
|
||||
CGFloat finalOpacity = view.layer.opacity;
|
||||
if ([createAnimation.property isEqualToString:@"scaleXY"]) {
|
||||
|
||||
NSString *property = createAnimation.property;
|
||||
if ([property isEqualToString:@"scaleXY"]) {
|
||||
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
||||
} else if ([createAnimation.property isEqualToString:@"opacity"]) {
|
||||
} else if ([property isEqualToString:@"opacity"]) {
|
||||
view.layer.opacity = 0.0;
|
||||
} else {
|
||||
RCTLogError(@"Unsupported layout animation createConfig property %@",
|
||||
createAnimation.property);
|
||||
}
|
||||
|
||||
[createAnimation performAnimations:^{
|
||||
if ([createAnimation.property isEqual:@"scaleXY"]) {
|
||||
if ([property isEqualToString:@"scaleXY"]) {
|
||||
view.layer.transform = finalTransform;
|
||||
} else if ([createAnimation.property isEqual:@"opacity"]) {
|
||||
} else if ([property isEqualToString:@"opacity"]) {
|
||||
view.layer.opacity = finalOpacity;
|
||||
} else {
|
||||
RCTLogError(@"Unsupported layout animation createConfig property %@",
|
||||
createAnimation.property);
|
||||
}
|
||||
|
||||
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
|
||||
if (updateBlock) {
|
||||
updateBlock(self, _viewRegistry);
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:completion];
|
||||
|
||||
// Animate view update
|
||||
} else if (updateAnimation) {
|
||||
|
||||
// Animate view update
|
||||
[updateAnimation performAnimations:^{
|
||||
[view reactSetFrame:frame];
|
||||
|
||||
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
|
||||
if (updateBlock) {
|
||||
updateBlock(self, _viewRegistry);
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:completion];
|
||||
|
||||
// Update without animation
|
||||
} else {
|
||||
[view reactSetFrame:frame];
|
||||
|
||||
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
|
||||
// Update without animation
|
||||
[view reactSetFrame:frame];
|
||||
if (updateBlock) {
|
||||
updateBlock(self, _viewRegistry);
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
completion(YES);
|
||||
}
|
||||
}
|
||||
|
||||
_layoutAnimation = nil;
|
||||
// Clean up
|
||||
uiManager->_layoutAnimation = nil;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -802,10 +808,11 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
|
|||
// the view events anyway.
|
||||
view.userInteractionEnabled = NO;
|
||||
|
||||
NSString *property = deleteAnimation.property;
|
||||
[deleteAnimation performAnimations:^{
|
||||
if ([deleteAnimation.property isEqual:@"scaleXY"]) {
|
||||
if ([property isEqualToString:@"scaleXY"]) {
|
||||
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
||||
} else if ([deleteAnimation.property isEqual:@"opacity"]) {
|
||||
} else if ([property isEqualToString:@"opacity"]) {
|
||||
view.layer.opacity = 0.0;
|
||||
} else {
|
||||
RCTLogError(@"Unsupported layout animation createConfig property %@",
|
||||
|
|
Loading…
Reference in New Issue