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 leaks in ContentView #15193

Closed

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented May 19, 2023

Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS
Related to: #14664

There are a couple cycles on iOS that causes memory leaks in all controls based on ContentView:

  • Microsoft.Maui.Controls.ContentView ->
    • ContentViewHandler ->
    • Microsoft.Maui.Platform.ContentView ->
    • CrossPlatformArrange/Measure property ->
  • Microsoft.Maui.Controls.ContentView cycle

To fix this, I made CrossPlatformArrange/Measure properties obsolete, using the weak View property directly instead. This applies to various other controls like Border, RadioButton, SwipeItemView, SwipeView.

I did not yet fix ScrollView that appears to be more complicated to solve. For now, it supports both code paths.

ScrollView will continue to have leaks in:

@jonathanpeppers jonathanpeppers marked this pull request as ready for review May 20, 2023 00:51
@Eilon Eilon added the area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter label May 22, 2023
Comment on lines 181 to +190
View = scrollView.PresentedContent,
#pragma warning disable CS0618 // Type or member is obsolete
CrossPlatformMeasure = ConstrainToScrollView(scrollView.CrossPlatformMeasure, platformScrollView, scrollView),
#pragma warning restore CS0618
Tag = ContentPanelTag
};

#pragma warning disable CS0618 // Type or member is obsolete
contentContainer.CrossPlatformArrange = ArrangeScrollViewContent(scrollView.CrossPlatformArrange, contentContainer, platformScrollView, scrollView);
#pragma warning restore CS0618
Copy link
Member

Choose a reason for hiding this comment

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

@hartez Is this something we need to consider for this PR? @jonathanpeppers will this still cause scroll views to live forever?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we need a new way that allows for also attaching static methods? This way we can still use the delegates, but be static so they don't capture anything?

Copy link
Member

Choose a reason for hiding this comment

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

@PureWeen what were your findings for using static methods? Did that help?

Copy link
Member

@PureWeen PureWeen May 24, 2023

Choose a reason for hiding this comment

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

Yea, if I was able to make a delegate static that would always fix the issue. I think this came down to how much we want to try and protect someone (us?) that's using these APIs. Instead of using a delegate, if you just have to set the View that's a path that's easy for us to make safe. That being said, it is possible to just use statics and make these not leak. Closures being very leaky on iOS isn't anything that's going away, so if you're writing iOS code and you don't have proper tests, or know how to make code not leak it's probably going to leak.

From a design perspective, if we favor the delegates I would be in favor of keeping them and relying on testing and possibly future tooling to keep us safe.

Copy link
Member Author

Choose a reason for hiding this comment

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

But you can still create leaks with static methods, see the closures here:

return (widthConstraint, heightConstraint) =>

Should we just avoid using all delegates? Otherwise we need to make it a build error if you use non-static ones -- or closures.

Comment on lines +102 to +104
[Obsolete("Use View instead. This member causes memory leaks.")]
internal Func<double, double, Size>? CrossPlatformMeasure { get; set; }
[Obsolete("Use View instead. This member causes memory leaks.")]
Copy link
Member

Choose a reason for hiding this comment

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

@jonathanpeppers @hartez since this is internal, should we maybe change the API so we can use a static delegate and rather pass around some sort of state - I am looking at the code for scroll view and the fact that it seems to sort of make some decisions with these 2 delegates.

Copy link
Member Author

Choose a reason for hiding this comment

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

I left these here for ScrollView to be fixed later.

How do we enforce the delegate will always be static?

src/Core/src/PublicAPI/net-ios/PublicAPI.Shipped.txt Outdated Show resolved Hide resolved
@hartez
Copy link
Contributor

hartez commented May 24, 2023

I've got an alternative version of this in the works that I think fixes the problem and preserves the original design intent. Hopefully I can get a PR in soon, I've just got to put out some other fires first. Then we can compare and see how we feel about each option.

@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented May 24, 2023

@hartez do you have any qualms about merging this, and add your changes later? We have an email thread that says "CUSTOMER URGENT DTS" in all caps.

@rmarinho
Copy link
Member

/rebase

@rmarinho
Copy link
Member

/rebase

Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS
Related to: dotnet#14664

There are a couple cycles on iOS that causes memory leaks in all
controls based on `ContentView`:

* `Microsoft.Maui.Controls.ContentView` ->
    * `ContentViewHandler` ->
    * `Microsoft.Maui.Platform` ->
    * `CrossPlatformArrange/Measure` property ->
* `Microsoft.Maui.Controls.ContentView` cycle

To fix this, I made `CrossPlatformArrange/Measure` properties obsolete,
using the weak `View` property directly instead. This applies to various
other controls like `Border`, `RadioButton`, `SwipeItemView`, `SwipeView`.

I did not yet fix `ScrollView` that appears to be more complicated to
solve. For now, it supports both code paths.

`ScrollView` will continue to have leaks in:

* Closures with captured variables
    * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L195
    * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L227

* `CrossPlatformArrange/Measure`
    * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L182-L186
@jonathanpeppers
Copy link
Member Author

Closing this one in favor of: #15303

As the other one also resolves issues around ScrollView.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter platform/iOS 🍎
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants