[iOS] Fix MauiCALayer and StaticCAShapeLayer crash on finalizer thread#33818
Conversation
There was a problem hiding this comment.
Pull request overview
This PR prevents EXC_BAD_ACCESS crashes on the finalizer thread for iOS MauiCALayer and StaticCAShapeLayer by making their dispose logic finalizer-safe, aligning with the standard .NET dispose pattern for objects that wrap UIKit-managed native layers.
Changes:
- Update
MauiCALayer.Dispose(bool disposing)to only call_autosizeToSuperLayerBehavior.Detach()whendisposing == true, avoiding native calls during finalization. - Update
StaticCAShapeLayer.Dispose(bool disposing)similarly to guard theDetach()call behindif (disposing)while keepingRemoveFromSuperLayerbehavior unchanged.
For a follow-up (not blocking this PR), consider applying the same if (disposing) guard in Dispose for other IAutoSizableCALayer implementations using MauiCALayerAutosizeToSuperLayerBehavior (e.g., StaticCALayer, StaticCAGradientLayer) to ensure consistent finalizer safety across all such layers.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Core/src/Platform/iOS/MauiCALayer.cs |
Makes Dispose(bool) finalizer-safe by only detaching the autosize behavior when explicitly disposing, preventing Obj-C calls on potentially deallocated CALayer instances. |
src/Core/src/Platform/iOS/StaticCAShapeLayer.cs |
Applies the same guarded-detach pattern in Dispose(bool) for StaticCAShapeLayer to avoid finalizer-thread crashes while preserving detach behavior in RemoveFromSuperLayer. |
albyrock87
left a comment
There was a problem hiding this comment.
Thanks for your first contribution!
The fix looks good though there should be other classes to be fixed: look for who implements IAutoSizableCALayer.
📋 PR Finalization ReviewTitle: ✅ GoodCurrent: Description: ✅ GoodDescription needs updates. See details below. **
Recommendation: Add NOTE block at top, clean up formatting. Keep all existing content - it's well-written and accurate. ✨ Suggested PR Description<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you!
</details> |
@dotnet-policy-service agree |
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
c8ea704 to
4d7dac7
Compare
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
#33818) ### Description of Change `MauiCALayer` and `StaticCAShapeLayer` crash with `EXC_BAD_ACCESS` when disposed on the finalizer thread in Release/AOT builds. The crash occurs because `Dispose()` unconditionally calls `_autosizeToSuperLayerBehavior.Detach()`, which invokes Objective-C methods on native `CALayer` objects that may have already been deallocated by UIKit. The fix wraps the `Detach()` call in `if (disposing)` to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization. ### Issues Fixed Fixes #33800 ### Changes - `src/Core/src/Platform/iOS/MauiCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAShapeLayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCALayer.cs` - Wrap `Detach()` call in `if (disposing)` - `src/Core/src/Platform/iOS/StaticCAGradientLayer.cs` - Wrap `Detach()` call in `if (disposing)` ### Testing - Built MAUI from source with this fix - - Tested in a production iOS app (Release/AOT via TestFlight) - - Confirmed crashes no longer occur with the patched `Microsoft.Maui.Core`
## What's Coming .NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 24 commits with various improvements, bug fixes, and enhancements. ## Animation - [Android] Fixed TransformProperties issue when a wrapper view is present by @Ahamed-Ali in #29228 <details> <summary>🔧 Fixes</summary> - [Android Image.Scale produces wrong layout](#7432) </details> ## Button - Fix ImageButton not rendering correctly based on its bounds by @Shalini-Ashokan in #28309 <details> <summary>🔧 Fixes</summary> - [ImageButton dosen't scale Image correctly](#25558) - [ButtonImage width not sizing correctly](#14346) </details> ## CollectionView - [Android] Fixed issue where group Header/Footer template was applied to all items when IsGrouped was true for an ObservableCollection by @Tamilarasan-Paranthaman in #28886 <details> <summary>🔧 Fixes</summary> - [[Android] Group Header/Footer Repeated for All Items When IsGrouped is True for ObservableCollection](#28827) </details> - [Android] CollectionView: Fix reordering when using DataTemplateSelector by @NanthiniMahalingam in #32349 <details> <summary>🔧 Fixes</summary> - [[Android][.NET9] CollectionView Reorderer doesn't work when using TemplateSelector](#32223) </details> - [Android] Fix for incorrect scroll position when using ScrollTo with a header in CollectionView by @SyedAbdulAzeemSF4852 in #30966 <details> <summary>🔧 Fixes</summary> - [Potential off-by-one error when using ScrollTo in CollectionView with a header.](#18389) </details> - Fix Incorrect Scrolling Behavior in CollectionView ScrollTo Method Using Index Value by @Shalini-Ashokan in #27246 <details> <summary>🔧 Fixes</summary> - [CollectionView ScrollTo not working under android](#27117) </details> - [Android] Fix System.IndexOutOfRangeException when scrolling CollectionView with image CarouselView by @devanathan-vaithiyanathan in #31722 <details> <summary>🔧 Fixes</summary> - [System.IndexOutOfRangeException when scrolling CollectionView with image CarouselView](#31680) </details> - [Android] Fix VerticalOffset Update When Modifying CollectionView.ItemsSource While Scrolled by @devanathan-vaithiyanathan in #26782 <details> <summary>🔧 Fixes</summary> - [CollectionView.Scrolled event offset isn't correctly reset when items change on Android](#21708) </details> ## Editor - Fixed Editor vertical text alignment not working after toggling IsVisible by @NanthiniMahalingam in #26194 <details> <summary>🔧 Fixes</summary> - [Editor vertical text alignment not working after toggling IsVisible](#25973) </details> ## Entry - [Android] Fix Numeric Entry not accepting the appropriate Decimal Separator by @devanathan-vaithiyanathan in #27376 <details> <summary>🔧 Fixes</summary> - [Numeric Entry uses wrong decimal separator in MAUI app running on Android](#17152) </details> - [Android & iOS] Entry/Editor: Dismiss keyboard when control becomes invisible by @prakashKannanSf3972 in #27340 <details> <summary>🔧 Fixes</summary> - [android allows type into hidden Entry control](#27236) </details> ## Gestures - [Android] Fixed PointerGestureRecognizer not triggering PointerMoved event by @KarthikRajaKalaimani in #33889 <details> <summary>🔧 Fixes</summary> - [PointerGestureRecognizer does not fire off PointerMove event on Android](#33690) </details> - [Android] Fix PointerMoved and PointerReleased not firing in PointerGestureRecognizer by @KarthikRajaKalaimani in #34209 <details> <summary>🔧 Fixes</summary> - [PointerGestureRecognizer does not fire off PointerMove event on Android](#33690) </details> ## Navigation - [Android] Shell: Fix OnBackButtonPressed not firing for navigation bar back button by @kubaflo in #33531 <details> <summary>🔧 Fixes</summary> - [OnBackButtonPressed not firing for Shell Navigation Bar button in .NET 10 SR2](#33523) </details> ## Shell - [iOS] Fixed Shell Navigating event showing same current and target values by @Vignesh-SF3580 in #25749 <details> <summary>🔧 Fixes</summary> - [OnNavigating wrong target when tapping the same tab](#25599) </details> - [iOS, macOS] Fixed Shell Flyout Icon is always black in iOS 26 by @Dhivya-SF4094 in #32997 <details> <summary>🔧 Fixes</summary> - [Shell Flyout Icon is always black](#32867) - [[iOS] Color Not Applied to Flyout Icon or Title on iOS 26](#33971) </details> ## TitleView - [Android] Fixed duplicate title icon when setting TitleIconImageSource Multiple times by @SubhikshaSf4851 in #31487 <details> <summary>🔧 Fixes</summary> - [[Android] Duplicate Title Icon Appears When Setting NavigationPage.TitleIconImageSource Multiple Times](#31445) </details> ## WebView - Fixed the crash on iOS when setting HeightRequest on WebView inside a ScrollView with IsVisible set to false by @Ahamed-Ali in #29022 <details> <summary>🔧 Fixes</summary> - [Specifying HeightRequest in Webview when wrapped by ScrollView set "invisible" causes crash in iOS](#26795) </details> <details> <summary>🧪 Testing (3)</summary> - [Testing] Fix for enable uitests ios26 by @TamilarasanSF4853 in #33686 - [Testing] Fixed Test case failure in PR 34173 - [02/21/2026] Candidate - 1 by @TamilarasanSF4853 in #34192 - [Testing] Fixed Test case failure in PR 34173 - [02/21/2026] Candidate - 2 by @TamilarasanSF4853 in #34233 </details> <details> <summary>📦 Other (3)</summary> - Fix Glide IllegalArgumentException in PlatformInterop for destroyed activities by @jonathanpeppers via @Copilot in #33805 - [iOS] Fix MauiCALayer and StaticCAShapeLayer crash on finalizer thread by @pshoey in #33818 <details> <summary>🔧 Fixes</summary> - [[iOS] MauiCALayer and StaticCAShapeLayer crash on finalizer thread in Release/AOT builds](#33800) </details> - Merge branch 'main' into inflight/candidate in 1a00f12 </details> **Full Changelog**: main...inflight/candidate --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> Co-authored-by: pshoey <pshoey@users.noreply.github.com> Co-authored-by: Subhiksha Chandrasekaran <subhiksha.c@syncfusion.com> Co-authored-by: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Co-authored-by: prakashKannanSf3972 <127308739+prakashKannanSf3972@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Co-authored-by: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Co-authored-by: NanthiniMahalingam <105482474+NanthiniMahalingam@users.noreply.github.com> Co-authored-by: BagavathiPerumal <bagavathiperumal.a@syncfusion.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com> Co-authored-by: Shalini-Ashokan <shalini.ashokan@syncfusion.com> Co-authored-by: Tamilarasan Paranthaman <93904422+Tamilarasan-Paranthaman@users.noreply.github.com> Co-authored-by: SyedAbdulAzeemSF4852 <syedabdulazeem.a@syncfusion.com> Co-authored-by: Ahamed-Ali <102580874+Ahamed-Ali@users.noreply.github.com> Co-authored-by: Dhivya-SF4094 <127717131+Dhivya-SF4094@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com> Co-authored-by: Matthew Leibowitz <mattleibow@live.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: TamilarasanSF4853 <tamilarasan.velu@syncfusion.com>
|
/backport to release/10.0.1xx-sr3 |
|
Started backporting to |
Description of Change
MauiCALayerandStaticCAShapeLayercrash withEXC_BAD_ACCESSwhen disposed on the finalizer thread in Release/AOT builds. The crash occurs becauseDispose()unconditionally calls_autosizeToSuperLayerBehavior.Detach(), which invokes Objective-C methods on nativeCALayerobjects that may have already been deallocated by UIKit.The fix wraps the
Detach()call inif (disposing)to follow the standard .NET dispose pattern - only call into external/native objects during explicit disposal, not during finalization.Issues Fixed
Fixes #33800
Changes
src/Core/src/Platform/iOS/MauiCALayer.cs- WrapDetach()call inif (disposing)src/Core/src/Platform/iOS/StaticCAShapeLayer.cs- WrapDetach()call inif (disposing)src/Core/src/Platform/iOS/StaticCALayer.cs- WrapDetach()call inif (disposing)src/Core/src/Platform/iOS/StaticCAGradientLayer.cs- WrapDetach()call inif (disposing)Testing
Microsoft.Maui.Core