[ios] fix memory leaks in GestureRecognizers#21089
[ios] fix memory leaks in GestureRecognizers#21089jonathanpeppers wants to merge 1 commit intodotnet:mainfrom
GestureRecognizers#21089Conversation
Context: dotnet#20023 (comment) One of the underlying reasons `RadioButton` appears to leak, is that it happens to use a `TapGestureRecognizer` internally. When testing, @tj-devel709 and I found we could add pretty much *any* recognizer to a simple control like `BoxView`, and it would cause the `BoxView` to live forever. I could reproduce this in a new unit test that I setup for each type of gesture recognizer. `GesturePlatformManager.iOS.cs` had two "cycles": * `*Handler` -> ... -> `GesturePlatformManager` -> `*Handler` * `*GestureRecognizer` -> `GesturePlatformManager` -> `*GestureRecognizer` To fix these, we can make `GesturePlatformManager` not have strong references pointing *back* to its parent classes: * Make `_handler` a `WeakReference<IPlatformViewHandler>` * Make `_gestureRecognizers` a `ConditionalWeakTable` With these changes the tests pass, except for `PanGestureRecognizer`, which had a "closure" in using the `panRecognizer` variable. I fixed this one with: * `((PanGestureRecognizer)panGestureRecognizer)` as `panGestureRecognizer` was backed by a `WeakReference`.
|
This one we probably need to go manually test all the recognizers. Is there a sample I can try that? |
I don't think so, Device Tests don't really support gestures so we haven't really used them very heavily other than basic logical tests. |
|
Ok, it turns out in practice that Tested with dotnet/maui/main: Something like this works in the sample: I will re-think this one. There is still an unsolved issue with Note there is no |
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.
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): * #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. Other changes: * `propertyChanging` seems wrong? Reading the existing code, it should be checking `oldvalue` instead of `newvalue`?
Context: #20023 (comment)
One of the underlying reasons
RadioButtonappears to leak, is that it happens to use aTapGestureRecognizerinternally. When testing, @tj-devel709 and I found we could add pretty much any recognizer to a simple control likeBoxView, and it would cause theBoxViewto live forever.I could reproduce this in a new unit test that I setup for each type of gesture recognizer.
GesturePlatformManager.iOS.cshad two "cycles":*Handler-> ... ->GesturePlatformManager->*Handler*GestureRecognizer->GesturePlatformManager->*GestureRecognizerTo fix these, we can make
GesturePlatformManagernot have strong references pointing back to its parent classes:Make
_handleraWeakReference<IPlatformViewHandler>Make
_gestureRecognizersaConditionalWeakTableWith these changes the tests pass, except for
PanGestureRecognizer, which had a "closure" in using thepanRecognizervariable.I fixed this one with:
((PanGestureRecognizer)panGestureRecognizer)aspanGestureRecognizerwas backed by aWeakReference.