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

[ios] fix memory leak in Button #14280

Merged
merged 1 commit into from
Apr 4, 2023

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Mar 29, 2023

Fixes #12048
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS
Context: https://stackoverflow.com/a/13059140

Issue #12048 has four scenarios that are currently fixed in main for Android and Windows. Unfortunately, one of the four scenarios fails on iOS due to its usage of Button. I could reproduce the issue by simply adding a Button to existing memory leak tests.

ButtonHandler.iOS.cs has a "cycle":

  • ButtonHandler subscribes to events like UIButton.TouchUpInside making UIButton hold a strong reference to ButtonHandler.

  • ButtonHandler (like all handlers) has a strong reference to the Microsoft.Maui.Button.

  • ButtonHandler has a strong reference to UIButton.

  • Microsoft.Maui.Button has a strong reference to ButtonHandler.

To solve this issue, we can make a nested classed named ButtonProxy:

  • Its purpose is subscribe to UIButton.TouchUpInside and invoke Microsoft.Maui.Button.Clicked().

  • ButtonProxy has a weak reference to the Microsoft.Maui.Button.

Now the UIButton, ButtonProxy, ButtonHandler, and Microsoft.Maui.Button can all be collected.

Fixes: dotnet#12048
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS
Context: https://stackoverflow.com/a/13059140

Issue dotnet#12048 has four scenarios that are currently fixed in `main` for
Android and Windows. Unfortunately, one of the four scenarios fails on
iOS due to its usage of `Button`. I could reproduce the issue by
simply adding a `Button` to existing memory leak tests.

`ButtonHandler.iOS.cs` has a "cycle":

* `ButtonHandler` subscribes to events like `UIButton.TouchUpInside`
  making `UIButton` hold a strong reference to `ButtonHandler`.

* `ButtonHandler` (like all handlers) has a strong reference to the
  `Microsoft.Maui.Button`.

* `ButtonHandler` has a strong reference to `UIButton`.

* `Microsoft.Maui.Button` has a strong reference to `ButtonHandler`.

To solve this issue, we can make a nested classed named `ButtonProxy`:

* Its purpose is subscribe to `UIButton.TouchUpInside` and invoke
  `Microsoft.Maui.Button.Clicked()`.

* `ButtonProxy` has a weak reference to the `Microsoft.Maui.Button`.

Now the `UIButton`, `ButtonProxy`, `ButtonHandler`, and
`Microsoft.Maui.Button` can all be collected.
@@ -166,20 +164,48 @@ static void SetControlPropertiesFromProxy(UIButton platformView)
}
}

void OnButtonTouchUpInside(object? sender, EventArgs e)
class ButtonEventProxy
Copy link
Contributor

Choose a reason for hiding this comment

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

There are several controls that can benefit from a similar change. I can create a list with the ones with the greatest impact.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you file a new issue with the list? I'll be going on vacation soon, so probably want to look at it when I get back.

@jonathanpeppers jonathanpeppers marked this pull request as ready for review March 30, 2023 13:41
@jonathanpeppers
Copy link
Member Author

I heard close/reopen PR is a way to fix the license/cla status:

image

Trying it, but I wonder if it will rerun all the CI again...

@Eilon Eilon added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Mar 31, 2023
@mattleibow mattleibow added the legacy-area-perf Startup / Runtime performance label Apr 4, 2023
{
VirtualView?.Released();
}
IButton? VirtualView => _virtualView is not null && _virtualView.TryGetTarget(out var v) ? v : null;
Copy link
Member

Choose a reason for hiding this comment

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

@PureWeen should this rather be at the base level for all handlers? Why are handlers holding strong references to virtual views when the virtual view decides what hander it is? Or is it the other way around?

At least for iOS, we should do this at the base level so that we can avoid this since all controls with events will do this.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should perhaps do both? From other cases where we've passed something into these special classes, we usually pass the handler in and then make it a weak reference to the handler.

For example this.

This special class is going to have to hold a weak reference to something. So, I don't think generalizing this inside the base of the handler will really change much of the code here.

@PureWeen PureWeen merged commit 58e05d0 into dotnet:main Apr 4, 2023
@jonathanpeppers jonathanpeppers deleted the ios-button-leaks branch April 10, 2023 04:45
@jonathanpeppers jonathanpeppers added memory-leak 💦 Memory usage grows / objects live forever and removed legacy-area-perf Startup / Runtime performance labels Jul 12, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 12, 2023
@Eilon Eilon added area-controls-button Button, ImageButton and removed legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor labels May 13, 2024
@samhouts samhouts added the fixed-in-8.0.0-preview.4.8333 Look for this fix in 8.0.0-preview.4.8333! label Aug 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-controls-button Button, ImageButton fixed-in-8.0.0-preview.4.8333 Look for this fix in 8.0.0-preview.4.8333! memory-leak 💦 Memory usage grows / objects live forever platform/iOS 🍎
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Possible Memory leaks?
7 participants