Open spouliot opened 2 years ago
The fact it's easier to dupe on macOS is because the bindings include custom code
[Export ("removeFromSuperview")]
[PreSnippet ("var mySuper = Superview;", Optimizable = true)]
[PostSnippet ("if (mySuper != null) {\n\t#pragma warning disable 168\n\tvar flush = mySuper.Subviews;\n#pragma warning restore 168\n\t}", Optimizable = true)]
void RemoveFromSuperview ();
AFAIK this is not needed anymore (since newrefcount become the default with the unified API). However I can't see/blame the source code from the era to confirm. If that's the case then NSView
has a few more places that needs scrubbing.
On iOS the warning appears because there are calls to the Superview
property (in between the first Dispose()
(from finalizer) and the second Dispose()
calls (dispatched from the first one).
IOW the call to RemoveFromSuperview
is not the trigger of the problem on iOS, just on macOS. This confirms the extra, custom code on the macOS side is triggering the issue.
reference: https://github.com/unoplatform/uno/issues/8688#issuecomment-1133800352
Using the following code, just after the call to GC.ReRegisterForFinalize(this);
solves the issue. IOW the original managed instance is returned by GetNSObject
.
const byte InFinalizerQueue = 16; // see NSObject2.cs
var poker = Unsafe.As<NSObjectMemoryRepresentation>(this);
poker.flags = (byte)(poker.flags & ~InFinalizerQueue);
However I can't see/blame the source code from the era to confirm.
It was added here: https://gist.github.com/rolfbjarne/b44c6e86bca3a9f46997a4d8f6a1aaf9
I created a PR to remove those Pre/Post snippets: #15103.
That doesn't fix the underlying problem though (resurrected managed peers only half resurrected), so I'll leave this bug open for now.
note: this does not seems to be an issue for net6-macos, which uses coreclr and a different mechanism to get notified
Steps to Reproduce
NSObject
subclass is finalizedobject_queued_for_finalization
and setNSObjectFlagsInFinalizerQueue
in the instance's flagsDispose(false)
callsGC.ReRegisterForFinalize(this);
NSObjectFlagsInFinalizerQueue
setRemoveFromSuperview
)GetNSObject
is used to get the instance,TryGetNSObject
looks for one usingevenInFinalizerQueue: false
NSObjectFlagsInFinalizerQueue
is set the method returnsnull
xamarin_set_gchandle_with_flags_safe
will find aGCHandle
(because a managed peer exists) and returnfalse
Expected Behavior
The warning should not be printed and it the condition it checks should not occur.
Actual Behavior
The logs shows the warning
Tried to create a managed reference from an object that already has a managed reference
. SomeNSObject
instances are resurfaced needlessly since they already exists in managed land.Environment
The issue exists in
master
Notes
Build Logs
Not useful.
Example Project (If Possible)
It's easier to repro using Xamarin.Mac since
NSView.RemoveFromSuperview
calls theSuperview
property. However the warning is also seen on iOS but the conditions might be a little different.Building Uno SampleApp for macOS and selecting the Border sample will print the warning. The generated
Dispose
can be seen in the generator code.If needed I'll try to create a smaller sample. I had no luck earlier (on iOS) but I had not yet found the root issue.