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 Image #15062

Merged
merged 8 commits into from
Jun 14, 2023
Merged

Conversation

jonathanpeppers
Copy link
Member

Context: #14664 (comment)

Image has two different "cycles" on iOS:

  • ImageHandler -> MauiImageView -> ImageHandler

    • via the WindowChanged event
  • ImageHandler -> ImageSourcePartLoader -> ImageHandler

    • via Handler, Func<IImageSourcePart?>, and Action<PlatformImage?>

This causes any MauiImageView to live forever.

To solve these issues:

  • Get rid of the MauiImageView.WindowChanged event, and use a WeakReference to the handler instead.

  • ImageSourcePartLoader now only has a WeakReference to the handler.

  • This requires a new ISetImageHandler interface to be used by several handler types involving images.

Hopefully the changes here are using [Obsolete] correctly to make things backwards compatible & less breaking changes.

Unsure yet if this fully solves #14664, but at least one part of it.

@jonathanpeppers jonathanpeppers marked this pull request as ready for review May 15, 2023 15:33
@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented May 16, 2023

This does not fully solve: #14664

It appears there are other cycles in the customer sample:

image

Here is one of them:

readonly IControlsView _view;
object? _containerView;
object? _platformView;
object? _handler;

We can merge this one as a starting place, and maybe adding the views to the screen in my test would trigger more issues?

src/Core/src/Platform/ImageSourcePartLoader.cs Outdated Show resolved Hide resolved
@@ -7,18 +7,34 @@ namespace Microsoft.Maui.Platform
{
public class MauiImageView : UIImageView
{
WeakReference<ImageHandler>? _handler;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this can be a public property? This way we don't have to change API at all much - the event still works - we just don't use it? Sort of like the iOS bending of a weak delegate OR an event? We can still use the existing iOS constructors.

Copy link
Member

Choose a reason for hiding this comment

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

We can mark the bad one as obsolete just to let people know why - and us. Rather than just "it will be removed", maybe we can be "Use Handler instead. iOS platform views should not have strong references to handlers or cross-platform views."

src/Core/src/Handlers/Image/ISetImageHandler.cs Outdated Show resolved Hide resolved
{
if (_handler is not null && _handler.TryGetTarget(out var handler))
{
handler.NotifyWindowChanged();
Copy link
Member

Choose a reason for hiding this comment

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

I am not 100% happy with views reaching out into internal handlers as this means this view will never work for anything else, but for now maybe it is fine? The method of catching the iOS window change is not great and is a bit iffy - we could actually do all this in cross platform code. We now have a Window property. But this is more for another issue.

@PureWeen thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

If we made this one public on IImageHandler and gave it a proper name, it would mean other IImageHandler could work?

@rmarinho
Copy link
Member

/rebase

jonathanpeppers and others added 8 commits May 30, 2023 23:57
Context: dotnet#14664 (comment)

`Image` has two different "cycles" on iOS:

* `ImageHandler` -> `MauiImageView` -> `ImageHandler`

  * via the `WindowChanged` event

* `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler`

    * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>`

This causes any `MauiImageView` to live forever.

To solve these issues:

* Get rid of the `MauiImageView.WindowChanged` event, and use a
`WeakReference` to the handler instead.

* `ImageSourcePartLoader` now only has a `WeakReference` to the handler.

* This requires a new `ISetImageHandler` interface to be used by
several handler types involving images.

Hopefully the changes here are using `[Obsolete]` correctly to make
things backwards compatible & less breaking changes.

Unsure yet if this fully solves dotnet#14664, but at least one part of it.
Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
PlatformView.Image = obj;

void OnWindowChanged(object? sender, EventArgs e)
public void OnWindowChanged()
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 want this to be a public API ? doesn't seem consistent with other Handlers

Copy link
Member

@rmarinho rmarinho left a comment

Choose a reason for hiding this comment

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

My only comment is related with WindowChanged not sure if we want that public.

@jonathanpeppers
Copy link
Member Author

If it isn't public, then no one can implement IImageHandler?. I did it mainly because of this comment:

#15062 (comment)

But, let me know what to do to get this merged.

@rmarinho rmarinho merged commit 556f1c7 into dotnet:main Jun 14, 2023
28 checks passed
@jonathanpeppers jonathanpeppers deleted the ImageDoesNotLeak branch June 14, 2023 13:35
StephaneDelcroix pushed a commit that referenced this pull request Jun 19, 2023
* [ios] fix memory leak in Image

Context: #14664 (comment)

`Image` has two different "cycles" on iOS:

* `ImageHandler` -> `MauiImageView` -> `ImageHandler`

  * via the `WindowChanged` event

* `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler`

    * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>`

This causes any `MauiImageView` to live forever.

To solve these issues:

* Get rid of the `MauiImageView.WindowChanged` event, and use a
`WeakReference` to the handler instead.

* `ImageSourcePartLoader` now only has a `WeakReference` to the handler.

* This requires a new `ISetImageHandler` interface to be used by
several handler types involving images.

Hopefully the changes here are using `[Obsolete]` correctly to make
things backwards compatible & less breaking changes.

Unsure yet if this fully solves #14664, but at least one part of it.

* Update src/Core/src/Platform/ImageSourcePartLoader.cs

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>

* Update MauiImageView.cs

* Update MauiImageView.cs

* ISetImageHandler -> IImageSourcePartSetter

* IImageHandler.OnWindowChanged() is now public

* Fix IImageSourcePartSetter namespace

* MauiImageView._handler can be readonly

---------

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
@Redth Redth added the backport/suggested The PR author or issue review has suggested that the change should be backported. label Jun 20, 2023
@PureWeen PureWeen added the backport/NO This change should not be backported. It may break customers. label Jun 28, 2023
@PureWeen
Copy link
Member

There are currently a number of PRs that are all working together to fix the memory leak issues on iOS. Any of these PRs backported individually will most likely not fix the issues that users are currently seeing. We have a few reproductions that we are testing against where we still haven't quite fixed all of the memory leaks. Backporting this PR would most likely have a higher chance of causing regressions (38 files changed) than it would actually fixing the overall problem.

.NET8 is now part of VS so if you can verify your scenarios against .NET8 that will ensure we've fixed as many leaks as possible for .NET8.

If there are still leaks that users are finding in .NET8 after the release of .NET8 those will have a decent chance of being backported. We should be through the larger sets of changes by the time we hit .NET8 so post .NET8 changes should be smaller/safer.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
backport/NO This change should not be backported. It may break customers. backport/suggested The PR author or issue review has suggested that the change should be backported. memory-leak 💦 Memory usage grows / objects live forever platform/iOS 🍎
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants