diff --git a/src/UIKit/UITraitChangeObservable.cs b/src/UIKit/UITraitChangeObservable.cs
index 156e35ccc90..ec20e57aca9 100644
--- a/src/UIKit/UITraitChangeObservable.cs
+++ b/src/UIKit/UITraitChangeObservable.cs
@@ -26,6 +26,26 @@ public static Class [] ToClasses (params Type [] traits)
return Class.FromTypes (traits);
}
+ // Register the observing object with the toggle-ref GC bridge before we hand a
+ // reference to it to native code. Without this, the managed peer's toggle-ref status
+ // defaults to whatever xamarin_gc_toggleref_callback infers from -retainCount, which
+ // only reflects normal ObjC retains: it can't see that _UITraitChangeRegistry now also
+ // holds a reference to this object internally (observer registries are conventionally
+ // non-retaining, to avoid retain cycles with their observers). If -retainCount is 1 at
+ // the next GC, the bridge downgrades the peer to a weak GC handle and it can be
+ // collected while _UITraitChangeRegistry still references it, corrupting the shared
+ // registry (a crash then tends to surface later, in unrelated code that next touches
+ // the registry, rather than here). MarkDirty is idempotent and mirrors the pattern
+ // already used by UIControl.AddTarget, UIGestureRecognizer, and
+ // NSNotificationCenter.AddObserver for the same reason.
+ private static void MarkDirtyForTraitRegistration (IUITraitChangeObservable observable)
+ {
+ // NSObject.MarkDirty() is 'protected'; the (bool) overload is 'internal' and can be
+ // called from anywhere in this assembly, which is what we need from a static method
+ // on an unrelated interface.
+ (observable as NSObject)?.MarkDirty (false);
+ }
+
///
/// Registers a callback handler that will be executed when one of the specified traits changes.
///
@@ -39,6 +59,7 @@ public IUITraitChangeRegistration RegisterForTraitChanges (Type [] traits, Actio
internal static IUITraitChangeRegistration _RegisterForTraitChanges (IUITraitChangeObservable This, Type [] traits, Action handler)
{
+ MarkDirtyForTraitRegistration (This);
return _RegisterForTraitChanges (This, ToClasses (traits), handler);
}
@@ -56,6 +77,7 @@ public IUITraitChangeRegistration RegisterForTraitChanges (Action handler, params Type [] traits)
{
// Add an override with 'params', unfortunately this means reordering the parameters.
+ MarkDirtyForTraitRegistration (This);
return _RegisterForTraitChanges (This, ToClasses (traits), handler);
}
@@ -74,6 +96,7 @@ public IUITraitChangeRegistration RegisterForTraitChanges (Action (IUITraitChangeObservable This, Action handler)
where T : IUITraitDefinition
{
+ MarkDirtyForTraitRegistration (This);
return _RegisterForTraitChanges (This, ToClasses (typeof (T)), handler);
}
@@ -95,6 +118,7 @@ internal static IUITraitChangeRegistration _RegisterForTraitChanges (IUI
where T1 : IUITraitDefinition
where T2 : IUITraitDefinition
{
+ MarkDirtyForTraitRegistration (This);
return _RegisterForTraitChanges (This, ToClasses (typeof (T1), typeof (T2)), handler);
}
@@ -119,6 +143,7 @@ internal static IUITraitChangeRegistration _RegisterForTraitChanges
where T2 : IUITraitDefinition
where T3 : IUITraitDefinition
{
+ MarkDirtyForTraitRegistration (This);
return _RegisterForTraitChanges (This, ToClasses (typeof (T1), typeof (T2), typeof (T3)), handler);
}
@@ -146,6 +171,7 @@ internal static IUITraitChangeRegistration _RegisterForTraitChanges