From 20e2e12dce2f81b92e8682f128cd81e08469f485 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Tue, 6 Dec 2016 13:15:32 +0100 Subject: [PATCH] [C] detach Behaviors and Triggers on VE finalization (#555) * [C] detach Behaviors and Triggers on VE finalization * update docs --- Xamarin.Forms.Core.UnitTests/BehaviorTest.cs | 42 +++++++++++++++++++++- Xamarin.Forms.Core/BindableObject.cs | 8 +++++ Xamarin.Forms.Core/VisualElement.cs | 13 +++++++ .../Xamarin.Forms/VisualElement.xml | 16 +++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs b/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs index 78e0cfb..9021738 100644 --- a/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs +++ b/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs @@ -3,8 +3,9 @@ using NUnit.Framework; namespace Xamarin.Forms.Core.UnitTests { - internal class MockBehavior : Behavior where T:BindableObject + class MockBehavior : Behavior where T:BindableObject { + public static int AttachCount { get; set; } public bool attached; public bool detached; @@ -12,11 +13,13 @@ namespace Xamarin.Forms.Core.UnitTests { base.OnAttachedTo (bindable); attached = true; + AttachCount++; AssociatedObject = bindable; } protected override void OnDetachingFrom (BindableObject bindable) { + AttachCount--; detached = true; base.OnDetachingFrom (bindable); AssociatedObject = null; @@ -106,5 +109,42 @@ namespace Xamarin.Forms.Core.UnitTests collection.Remove (behavior); Assert.Null (behavior.AssociatedObject); } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=44074 + public void TestBehaviorsAreDetachedBeforeGarbageCollection() + { + WeakReference weakBindable = null; + + var attachCount = MockBehavior.AttachCount; + + int i = 0; + Action create = null; + create = () => + { + if (i++ < 1024) + { + create(); + return; + } + + var bindable = new MockBindable + { + Behaviors = { + new MockBehavior () + } + }; + weakBindable = new WeakReference(bindable); + }; + + create(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.False(weakBindable.IsAlive); + Assert.AreEqual(attachCount, MockBehavior.AttachCount); + } } } \ No newline at end of file diff --git a/Xamarin.Forms.Core/BindableObject.cs b/Xamarin.Forms.Core/BindableObject.cs index 217f4a9..b5e4145 100644 --- a/Xamarin.Forms.Core/BindableObject.cs +++ b/Xamarin.Forms.Core/BindableObject.cs @@ -163,6 +163,14 @@ namespace Xamarin.Forms return bpcontext != null && bpcontext.Binding != null; } + internal bool GetIsDefault(BindableProperty targetProperty) + { + if (targetProperty == null) + throw new ArgumentNullException(nameof(targetProperty)); + + return GetContext(targetProperty) == null; + } + internal object[] GetValues(BindableProperty property0, BindableProperty property1) { var values = new object[2]; diff --git a/Xamarin.Forms.Core/VisualElement.cs b/Xamarin.Forms.Core/VisualElement.cs index 053ddb0..cff1eb1 100644 --- a/Xamarin.Forms.Core/VisualElement.cs +++ b/Xamarin.Forms.Core/VisualElement.cs @@ -781,5 +781,18 @@ namespace Xamarin.Forms public bool Result { get; set; } } + + ~VisualElement() + { + if (!GetIsDefault(BehaviorsProperty)) { + var behaviors = GetValue(BehaviorsProperty) as AttachedCollection; + behaviors.DetachFrom(this); + } + + if (!GetIsDefault(TriggersProperty)) { + var triggers = GetValue(TriggersProperty) as AttachedCollection; + triggers.DetachFrom(this); + } + } } } \ No newline at end of file diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms/VisualElement.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms/VisualElement.xml index 426d8c6..9b28bf0 100644 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms/VisualElement.xml +++ b/docs/Xamarin.Forms.Core/Xamarin.Forms/VisualElement.xml @@ -290,6 +290,22 @@ + + + + Method + + 2.0.0.0 + + + System.Void + + + + To be added. + To be added. + + -- 2.7.4