Skip to content

[release/10.0.1xx-sr3] [iOS] Fix ObjectDisposedException in ContentView.RemoveContentMask#34744

Merged
PureWeen merged 7 commits intorelease/10.0.1xx-sr3from
backport/pr-34680-to-release/10.0.1xx-sr3
Mar 31, 2026
Merged

[release/10.0.1xx-sr3] [iOS] Fix ObjectDisposedException in ContentView.RemoveContentMask#34744
PureWeen merged 7 commits intorelease/10.0.1xx-sr3from
backport/pr-34680-to-release/10.0.1xx-sr3

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Backport of #34680 to release/10.0.1xx-sr3

/cc @PureWeen @Oxymoron290

Oxymoron290 and others added 7 commits March 30, 2026 18:04
Check Handle != IntPtr.Zero before calling RemoveFromSuperLayer() on
_contentMask to prevent ObjectDisposedException when iOS has already
deallocated the underlying CAShapeLayer during view hierarchy teardown.

This follows the established disposal guard pattern used throughout the
codebase (ViewRenderer.cs, LayoutFactory2.cs, CellRenderer.cs, etc.).

Fixes a crash reported in Active Trader Pro on iOS/MacCatalyst where
WillRemoveSubview triggers RemoveContentMask after the native layer
has been collected.

Related: #30861, #33818

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace conditional mask disposal with Assert.IsType<CAShapeLayer>
  to fail fast if the mask is not created (avoids false-positive)
- Assert Handle == IntPtr.Zero after disposal to verify test setup
- Remove customer issue link from test comment
- Add reference to upstream dotnet/macios#10562

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assert.IsType<CAShapeLayer> requires an exact type match, but _contentMask
is typed as StaticCAShapeLayer (a subclass of CAShapeLayer). This caused the
RemoveContentMaskDoesNotThrowWhenDisposed test to fail on iOS and MacCatalyst
with: 'Assert.IsType() Failure: Value is not the exact type'

Also improve Assert.True to Assert.Equal for better diagnostic output on failure.

Fixes: net10.0 iOS/MacCatalyst Helix Tests (Mono) device test failures

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ison

Assert.Equal(IntPtr.Zero, mask.Handle) fails on Apple TFMs because
mask.Handle returns nint (NativeHandle) and xUnit's overload resolution
falls through to Assert.Equal<DateTime>, producing CS1503. Use
Assert.True with equality check instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On real iOS/MacCatalyst devices, calling Dispose() on a CAShapeLayer
does not zero its Handle while another native reference (Layer.Mask)
still retains it. Clear the native reference first to properly simulate
iOS deallocating the layer during view teardown.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
With content.Layer.Mask cleared before Dispose(), the native retain
count drops to zero and Handle is properly zeroed. Re-add the
assertion to verify the guard condition is actually being tested.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of relying on platform-specific Dispose() behavior (which
varies depending on native retain counts), create a fresh CAShapeLayer
with no native retains, Dispose it (deterministically zeroing Handle),
then inject it into the private _contentMask field via reflection.

This guarantees Handle == IntPtr.Zero on all platforms and directly
tests the production guard in RemoveContentMask().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen
Copy link
Copy Markdown
Member

/azp run maui-pr-uitests, maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants