Skip to content

CarouselViewController2 leaks on iOS/MacCatalyst due to unremoved orientation notification observer #35472

Description

@AdamEssenmacher

Description

CarouselViewController2 subscribes to UIDevice.OrientationDidChangeNotification using the block-based NSNotificationCenter.DefaultCenter.AddObserver(...) overload, but it does not store the returned observer token.

During teardown, it calls:

NSNotificationCenter.DefaultCenter.RemoveObserver(this, UIDevice.OrientationDidChangeNotification, null);

That does not remove the block observer returned by AddObserver, so the notification center keeps the CarouselViewController2 callback target alive after the CarouselView is popped, detached, disconnected, and forced GC runs.

A repro shows that the CarouselView, handler, and platform view collect, while CarouselViewController2 remains alive. A CollectionView control scenario fully collects under the same attach/detach path.

Steps to Reproduce

  1. Clone the repro branch:
git clone https://github.com/AdamEssenmacher/maui.git
cd maui
git checkout repro/carousel-view-2-observer-leak
  1. Build the MacCatalyst repro:
./.dotnet/dotnet build repro/carouselview2-orientation-observer-leak-20260512/CarouselView2OrientationObserverLeakRepro.csproj -f net10.0-maccatalyst -p:ValidateXcodeVersion=false
  1. Run the generated app:
RID=maccatalyst-x64
if [ "$(uname -m)" = "arm64" ]; then RID=maccatalyst-arm64; fi
artifacts/bin/CarouselView2OrientationObserverLeakRepro/Debug/net10.0-maccatalyst/$RID/CarouselView2OrientationObserverLeakRepro.app/Contents/MacOS/CarouselView2OrientationObserverLeakRepro
  1. Check the result file:
cat ~/Library/carouselview2-orientation-observer-leak-result.txt

Expected Result

After the CarouselView page is popped, detached, disconnected, and forced GC runs, the CarouselViewController2 should be collected.

Actual Result

The repro exits with code 0 and reports Leak proved: True. The CarouselView, handler, and platform view collect, but CarouselViewController2 remains alive:

ViewAlive: False
HandlerAlive: False
ControllerAlive: True
PlatformViewAlive: False

The CollectionView control scenario fully collects under the same path.

Link to public reproduction project repository

https://github.com/AdamEssenmacher/maui/tree/repro/carousel-view-2-observer-leak/repro/carouselview2-orientation-observer-leak-20260512

Version with bug

10.0.60

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, macOS

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-controls-collectionviewCollectionView, CarouselView, IndicatorViewi/regressionThis issue described a confirmed regression on a currently supported versionpartner/syncfusionIssues / PR's with Syncfusion collaborationplatform/iosplatform/macosmacOS / Mac Catalystregressed-in-10.0.20s/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions