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 RadioButton #21151

Merged
merged 3 commits into from
Mar 18, 2024

Conversation

jonathanpeppers
Copy link
Member

Context: https://github.com/heyThorsten/GCTest
Fixes: #20023

In testing the above sample, a page with a RadioButton has a memory leak due to the usage of Border.StrokeShape.

There was also a slight "rabbit" hole, where we thought there was also an issue with GestureRecognizers. But this was not the case in a real app (just unit tests):

It turns out that when GestureRecognizers are used in MemoryTests, they will leak if there is no Window involved.

Instead of using MemoryTests like I would traditionally, we should instead use NavigationPageTests.DoesNotLeak(). I can reproduce the problem with RadioButton and a Window exists in the test.

Underlying issue

It appears that Border.StrokeShape does not use the same pattern as other properties like Border.Stroke:

  • On set: SetInheritedBindingContext(visualElement, BindingContext);
  • On unset: SetInheritedBindingContext(visualElement, null);

It instead used:

  • On set: AddLogicalChild(visualElement);
  • On unset: RemoveLogicalChild(visualElement);

6136a8a that introduced these does not mention why one was used over another. I am unsure if this will cause a problem, but it fixes the leak.

Context: https://github.com/heyThorsten/GCTest
Fixes: dotnet#20023

In testing the above sample, a page with a `RadioButton` has a memory
leak due to the usage of `Border.StrokeShape`.

There was also a slight "rabbit" hole, where we thought there was also
an issue with `GestureRecognizers`. But this was not the case in a real
app (just unit tests):

* dotnet#21089

It turns out that when `GestureRecognizers` are used in `MemoryTests`,
they will leak if there is no `Window` involved.

Instead of using `MemoryTests` like I would traditionally, we should
instead use `NavigationPageTests.DoesNotLeak()`. I can reproduce the
problem with `RadioButton` *and* a `Window` exists in the test.

~~ Underlying issue ~~

It appears that `Border.StrokeShape` does not use the same pattern as
other properties like `Border.Stroke`:

* On set: `SetInheritedBindingContext(visualElement, BindingContext);`
* On unset: `SetInheritedBindingContext(visualElement, null);`

It instead used:

* On set: `AddLogicalChild(visualElement);`
* On unset: `RemoveLogicalChild(visualElement);`

6136a8a that introduced these does not mention why one was used over
another. I am unsure if this will cause a problem, but it fixes the leak.
@jonathanpeppers
Copy link
Member Author

/cc @tj-devel709

@jonathanpeppers jonathanpeppers marked this pull request as ready for review March 13, 2024 14:32
@jonathanpeppers jonathanpeppers requested a review from a team as a code owner March 13, 2024 14:32
AddLogicalChild(visualElement);
SetInheritedBindingContext(visualElement, BindingContext);
Copy link
Member Author

Choose a reason for hiding this comment

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

@PureWeen maybe you remember if there is something this change would break? This makes Border.StrokeShape and Border.Stroke the same now.

Copy link
Member

@PureWeen PureWeen Mar 14, 2024

Choose a reason for hiding this comment

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

This should be fine

Prior to converting this to AddLogicalChild we were already setting the parent
https://github.com/dotnet/maui/blame/cd7a0e021d99be3ef8c7d83437fb66f5080cd5af/src/Controls/src/Core/Border/Border.cs#L67-L68

Which is why I converted it over to AddLogicalChild during that big push

Stephanes changes with these changes should make this all work alright.

The only weird thing here is if the strokes are static resources this code is still weird, but it's already weird and this makes it less weird at least.

Copy link
Member

Choose a reason for hiding this comment

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

do we need to copy the concept with parent resource propagation that's part of stroke?

OnParentResourcesChanged(this.GetMergedResources());
((IElementDefinition)this).AddResourcesChangedListener(stroke.OnParentResourcesChanged);

Copy link
Member Author

Choose a reason for hiding this comment

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

For Stroke, I guess you could have a GradientBrush that uses a {StaticResource} for a color inside. That seems like it would be required.

For StrokeShape, I guess you can do the exact thing for something like a double value on some shape, so I'll make the properties the same.

This makes `Border.StrokeShape` closer to existing code in `Border.Stroke`.
@jonathanpeppers jonathanpeppers added memory-leak 💦 Memory usage grows / objects live forever (sub: perf) platform/iOS 🍎 labels Mar 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RadioButton is not currently being Garbage Collected
3 participants