Skip to content
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

fix: avoid memory leak on iOS #4355

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

vladvlasov256
Copy link

Summary

The PR fixes memory leaks on iOS.

Screenshot 2024-12-31 at 10 19 08

The RCTVideo and ReactNativeVideoManager create a reference cycle because ReactNativeVideoManager retains the views but never release them.

Motivation

Reducing the memory usage in the apps.

Changes

Removes redundant instanceList in ReactNativeVideoManager but keep the counter logic. React Native retain the view references so no need to have an extra list.

Test plan

  1. In the basic app, push and pop the video screen 8 times.
  2. Check RCTVideo instances in the memory graph.

Expected result: only a single RCTVideo instance is presented.

Screenshot 2024-12-31 at 10 29 33



⚠️ In the sample app, the CLANG_ADDRESS_SANITIZER is enabled in debug. So Xcode doesn't show a proper object list. I had to remove Yes value for test purposes. Screenshot 2024-12-31 at 10 41 01

@vladvlasov256
Copy link
Author

There might be a bug in the instance counter logic on both platforms. Please take a look at the Android code:

 fun registerView(newInstance: ReactExoplayerViewManager) {
    if (instanceList.size > 2) {
        DebugLog.d(TAG, "multiple Video displayed ?")
    }
    instanceList.add(newInstance)
}

As I understand, a user gets a debug output only after adding the 4th video to the screen:

Added the 1st video. instanceList.size = 0, no warning.
Added the 2nd video. instanceList.size = 1, no warning.
Added the 3rd video. instanceList.size = 2, no warning.
Added the 4th video. instanceList.size = 3, the warning is displayed.

Should the condition be instanceList.size > 0 instead? So then the user gets the warning after adding the 2nd video.

@freeboub what do you think?

@@ -256,6 +256,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
ReactNativeVideoManager.shared.registerView(newInstance: self)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there 2 init functions 🤔
BTW looks good to me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That one with coder is for compatibility with older versions of swift and storyboard - this init in general should be never called

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this call. I added it just for init/deinit symmetry.

@freeboub
Copy link
Collaborator

There might be a bug in the instance counter logic on both platforms. Please take a look at the Android code:

 fun registerView(newInstance: ReactExoplayerViewManager) {
    if (instanceList.size > 2) {
        DebugLog.d(TAG, "multiple Video displayed ?")
    }
    instanceList.add(newInstance)
}

As I understand, a user gets a debug output only after adding the 4th video to the screen:

Added the 1st video. instanceList.size = 0, no warning. Added the 2nd video. instanceList.size = 1, no warning. Added the 3rd video. instanceList.size = 2, no warning. Added the 4th video. instanceList.size = 3, the warning is displayed.

Should the condition be instanceList.size > 0 instead? So then the user gets the warning after adding the 2nd video.

@freeboub what do you think?

Yes I may do better. I put this, as is, because, from my experience, 2 parallel video decoding on a device is working pretty fine in most of case... but It is not absolutely true. In another fork, I implemented a limitation to ensure we don't use more than 'X' Video in the same time by droping previous instance (for safety).

@vladvlasov256
Copy link
Author

Yes I may do better. I put this, as is, because, from my experience, 2 parallel video decoding on a device is working pretty fine in most of case... but It is not absolutely true

@freeboub, in this PR, the maximum value is changed from 10 to 2 to align it with Android. Does this make sense, or should I revert it?

@freeboub
Copy link
Collaborator

Yes I may do better. I put this, as is, because, from my experience, 2 parallel video decoding on a device is working pretty fine in most of case... but It is not absolutely true

@freeboub, in this PR, the maximum value is changed from 10 to 2 to align it with Android. Does this make sense, or should I revert it?

No that's good, ios is really more robust at this point (but it's not a reason) :)

Copy link
Member

@KrzysztofMoch KrzysztofMoch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine for me

Comment on lines -22 to +23
func registerView(newInstance: RCTVideo) {
if instanceList.count > expectedMaxVideoCount {
func registerView(newInstance _: RCTVideo) {
if instanceCount > expectedMaxVideoCount {
Copy link
Member

@KrzysztofMoch KrzysztofMoch Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this breaking change? If it is let's add "!" to commit -> fix!: avoid memory leak on iOS

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't break the existing API. But the argument is unused so _ makes the linter happy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants