-
Notifications
You must be signed in to change notification settings - Fork 5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[iOS] ExpoRequestInterceptorProtocol
implementation leads to memory leaks and excessive usage of URLSession
instances
#29561
Comments
@Kudo @brentvatne I have a work in progress branch with a proposed solution that I believe can work quite well. tl;dr: Intercept the calls made by the React's |
hi @hakonk, thanks for bringing this great issue. i did not notice the invalidateAndCancel / finishTasksAndInvalidate note until you mentioned to me. i had a fix in #29798 to fix the leakage by reusing the shared URLSession. as your branch that's not ideal for our case because we want to monitor all native requests than just react's XHR. hopes that makes sense to you. |
Hi, @Kudo! I'm glad you are addressing the issue with the leaks. There are however some other problematic things about the implementation that I would like to point out: From a test run of Bare Expo with malloc stack logging enabled, I can see in the memory graph that there are 45 instances of While you have found a solution to the problem with the leaked instances of Another problematic thing is that when swizzling the default If you want to intercept all network requests made in the app, I believe it is better to swizzle Edit: I can see that |
can you double check if the duplicate requests are true? i tested with mitmproxy and it looks only having single request to me. i may not familiar with enough for URLSession but my current understanding is that when please feel free to correct me or if i missed anything from you. thanks again for looking into the problem. |
I will look into it when I have the time, but it sounds like you are right. My apologies, I merely tested with breakpoints. I believe the issue of missing interception for other configuration than |
that's valid. having non-default URLSessionConfiguration is kind of out of my original scope. let's see if people have this requirement first. i guess i'll try to either expose ask people to manually add the |
i'm landing #29798 and close this issue. if there'are any further issues, please create new issue or we can still follow up here. i'm super glad to have you discussing issues with in-depth insight. |
Likewise! Thanks for the fix and the discussion. |
Summary
When observing the memory graph after in the Bare Expo app, I found something problematic. It seems several instances of
ExpoRequestInterceptorProtocol
were leaked. After looking at the implementation, it is clear why:The reason is pointed out in the Apple docs:
"The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session by calling the invalidateAndCancel() or finishTasksAndInvalidate() method, your app leaks memory until it exits."
There is nothing calling the above mentioned methods that would in turn invalidate the
URLSession
instance. Thus, there will be retain cycle.Another issue with the implementation is that each instance of
ExpoRequestInterceptorProtocol
will create its own instance ofURLSession
each time it is initialised. This is contrary to Apple's best practices for how to employURLSession
, i.e., one should not create a newURLSession
per request. Additionally, wouldn't this also mean that each request is sent twice?Perhaps it is better to method swizzle the
URLSessionDelegate
implementation ofRCTHTTPRequestHandler
instead?What platform(s) does this occur on?
iOS
SDK Version
No response
Environment
expo-env-info 1.2.0 environment info:
System:
OS: macOS 14.5
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.0.0 - ~/.nvm/versions/node/v22.0.0/bin/node
Yarn: 1.22.21 - ~/.nvm/versions/node/v22.0.0/bin/yarn
npm: 10.5.1 - ~/.nvm/versions/node/v22.0.0/bin/npm
Watchman: 2024.05.06.00 - /opt/homebrew/bin/watchman
Managers:
CocoaPods: 1.14.2 - /Users/user/.rvm/gems/ruby-2.7.2/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 23.5, iOS 17.5, macOS 14.5, tvOS 17.5, visionOS 1.2, watchOS 10.5
Android SDK:
API Levels: 26, 33, 34
Build Tools: 26.0.3, 30.0.3, 33.0.1, 34.0.0, 35.0.0
System Images: android-26 | Google APIs Intel x86_64 Atom, android-34 | Google APIs ARM 64 v8a
IDEs:
Android Studio: 2023.3 AI-233.14808.21.2331.11709847
Xcode: 15.4/15F31d - /usr/bin/xcodebuild
Expo Workflow: bare
Minimal reproducible example
ExpoRequestInterceptorProtocol
instances.The text was updated successfully, but these errors were encountered: