reports
reports copied to clipboard
FB7539293: SwiftUI view used as custom view in NSMenuItem is never released, causing huge memory leaks
- Date: 2020-01-17
- Resolution: Open
- Area: SwiftUI Framework
- OS: macOS 10.15.2
- Type: Incorrect/Unexpected Behavior
Description
I have a menu bar app where the user clicks a menu bar item and a NSMenu shows. Every time the menu is shown, I recreate the menu as it's highly dynamic. I'm using SwiftUI for some custom NSMenuItem views and those views are never released when I call NSMenu#removeAllItems(). This means that every time the user opens the menu, the memory doubles. This is especially problematic in my app as I use some heavy SwiftUI views, so in my app, the memory usage increases by 30 MB each time the menu is opened, without ever being released.
I have attached a very minimal sample project that reproduces the issue.
Steps to reproduce:
- Open the attached sample project.
- Build and run the project.
- Open Activity Monitor, click the Memory tab, and search for "NSStatusBarTestCase".
- Note the current memory usage. (12 MB for me)
- Click the "Click Me" menu bar item, wait until the menu opens (it takes some seconds), and then close the menu.
- Check Activity Monitor again and notice how the memory usage is much higher and never goes down. (70 MB for me)
I can reproduce this consistently both in debug and release builds.
I have also tried doing menu.items = [], but with the same result.
Files
My workaround: https://twitter.com/sindresorhus/status/1218959711661244416 😢
Looked into the sample project and did some instruments. Two things going on as far as I can see
NSHostingViewis leaking memory for every instance as a part of it registering itself to observe on some notifications by calling-[NSWorkspaceNotificationCenter addObserver:selector:name:object:].NSMenuItemis leaking memory for every instance by leaving many Malloc blocks of 32, 48, 64 and 80 bytes behind.
These are things that Apple should patch.
I think your best bet for a workaround if you don't want to force restart the app is to reuse instances of NSHostingView and NSMenuItem instead of recreating them. You can update the rootView property of an existing NSHostingView instance if you need to create a new instance of the view.
@petergp Thank you so much for commenting. I finally got a chance to try out your suggested workaround and it definitely improves the situation a lot. It's still leaking, but much less than before. 🙌
I have added your findings to the Feedback Assistant report. I have yet to get a reply from Apple though.
Does anyone happen to know if this has been addressed in the intervening 4.5 years? I was about to use SwiftUI views in an NSMenuItem, but stumbled across this problem and have now reconsidered.
Update: it appears that Apple resolved the issue. I've built your sample project using Xcode 16 beta 3 with the base macOS SDK and a deployment target of macOS 13.0+. Instruments reports no leaks through a series of clicks to open and dismiss the menu:
(An aside: the memory usage reported in Activity Monitor isn't a great way to diagnose actual usage by the app. I forget where I read this, but the usage reported in Activity Monitor is the app's current "high water mark" for memory usage and not necessarily how much it's using at that particular time. This may have changed in intervening macOS releases; I've been doing this too long.)
I am also facing the issue with latest version xcode 16.3 and mac os version 15.4.1 i am removing all the items except window and dynamically creating the menu items based on the active window.