Serious performance regression in iOS 9 and 10 when using SafeAreaView
🐛 Bug Report
When a ScrollView contains enough children that they will not all fit on the screen, a runaway thread causes CPU usage to increase to 100% for a quiescent app in a debug build. The problem also occurs in Release builds, with CPU use running an average 50-80%.
For more info, see this issue
To Reproduce
git clone https://github.com/Purii/react-native-tableview-simple.git
cd react-native-tableview-simple/example
# Change package.json to depend on version 2.1.0 of react-native-tableview-simple
# if it's pointing to a newer version
yarn
Open ios/example.xcodeproj in XCode. Select a simulator with iOS 9 or iOS 10. Build and run. Click on the Debug navigator in XCode. You will see the CPU usage at 100%.
Expected Behavior
CPU usage should be 0%. The app doesn't do anything but sit there and display some views.
Code Example
https://github.com/Purii/react-native-tableview-simple/tree/master/example
Environment
info React Native Environment Info: System: OS: macOS 10.14.4 CPU: (4) x64 Intel(R) Core(TM) i5-7600K CPU @ 3.80GHz Memory: 8.69 GB / 40.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 8.15.0 - ~/.nodenv/versions/8.15.0/bin/node Yarn: 1.15.2 - /usr/local/bin/yarn npm: 6.8.0 - ~/.nodenv/versions/8.15.0/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman SDKs: iOS SDK: Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2 Android SDK: API Levels: 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 28 Build Tools: 23.0.1, 23.0.2, 24.0.1, 26.0.3, 28.0.0, 28.0.3 IDEs: Android Studio: 2.1 AI-143.2915827 Xcode: 10.2/10E125 - /usr/bin/xcodebuild npmPackages: react: 16.8.3 => 16.8.3 react-native: 0.59.3 => 0.59.3 npmGlobalPackages: react-native-cli: 2.0.1 react-native-create-library: 1.0.4 react-native-git-upgrade: 0.2.7 react-native-version: 2.0.1
Any progress on this?
I notice that the build produces warnings like:
'UIScrollViewContentInsetAdjustmentBehavior' is only available on iOS 11.0 or newer
while compiling the file RCTScrollViewManager.m
/Users/maint/github/react-native-tableview-simple/example/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m:38:20: 'UIScrollViewContentInsetAdjustmentBehavior' is only available on iOS 11.0 or newer /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIScrollView.h:35:28: 'UIScrollViewContentInsetAdjustmentBehavior' has been marked as being introduced in iOS 11.0 here, but the deployment target is iOS 9.0.0 /Users/maint/github/react-native-tableview-simple/example/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m:38:1: Annotate 'UIScrollViewContentInsetAdjustmentBehavior:' with an availability attribute to silence this warning
This happens when using SafeAreaView. Testing shows the regression appeared in v0.58 when PR #18534 landed, changing the behavior of SafeAreaView on iOS 9 and 10.
@vovkasm, perhaps you could take a look.
The example uses SafeAreaView components as children of ScrollView. I'm not sure whether it is acceptable to use it this way, as the docs say,
Simply wrap your top level view with a SafeAreaView with a flex: 1 style applied to it.
If it is an error to use SafeAreaView in a non-top-level view, there ought to be a Yellow Box warning about it.
@chetstone SafeAreaView in current implementation inherently inefficient and can't be with current architecture – it needs first render pass to show UI then it will "know" safe area value and report back to layout thread, then it will rerender and modify UI second time.
Probably in scroll context layoutSubviews (in which emulation code was written) called too many times that expose gigantic slowness. This can be mitigated in native code, moreover safe area can be much more effective in principle (with some support from yoga and shadow tree), but I'm not very interested because even my small PR was modified to be slower without any constructive explanation.
But you can easily fix this without native programming. You can use SafeAreaView in background layer outside ScrollView and measure real safe area value in onLayout (or something like react-native-safe-area for monitor safe area insets on root view, which is most efficient today), then use calculated value as you wish.
@vovkasm Thanks for the comment. It doesn't seem like this is going to be fixed any time soon. The workaround, of course, is to not use SafeAreaView on iOS 9/10, as I did in this PR.
By the way, for those who come across this thread in the future, in answer to my other question above, the owner of react-native-tableview-simple has offered an explanation of how SafeAreaView in a not-top-level context can be useful.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.