Observed view model leaks when using UIViewController.present(item: ...)
#283
Labels
bug
Something isn't working
Description
Hey folks, I'm working on refactoring a big portion of our mixed SwiftUI and UIKit. To address the UIKit part, I'm slowing adopting
UIKitNavigation
but I'm running into a memory leak that I struggle to see how to fix.From my understanding of the problem the
present
helper is creating a retain cycle on the observed object.I've created a sample project demonstrating the leak. It includes a possible fix though it is less than ideal for general adoption of UIKitNavigation in our codebase.
Here's a few explanations for the sample project
To simplify the problem, I've created a simple example UIKit containing two VC
Root
andChildFeature
.Root
is based on vanilla UIKit code and patterns.ChildFeature
is the hierarchy of VCs I'm slowing refactoring to adopt state-based navigation and my first feature using it is leaking its view model onceChildFeatureVC
is dismissed by the Root.The issue I end up having is that If I present ChildFeatureViewController , user does its thing in that feature, then dismisses it. The ChildFeatureViewModel is retaining itself forever. So everytime I present, I end up with a new instance of the VM leaking.
I struggle to see how to resolve this retain cycle. The only thing I figured out is "emitting" a nil event in my VC 's deinit seems to break the cycle but this solution is far from idea.
MemLeakBinding.zip
Originally brought this up in the PFC Slack community: https://pointfreecommunity.slack.com/archives/C04L7QT8L2Y/p1743131001044919
Checklist
main
branch of this package.Expected behavior
When using
and the appropriate View controller is presented then, a few moments later dismissed. Both the View Controller and the observed object are released.
Actual behavior
Only the view controller is released. The observed object retains itself and never goes away. If it is for a View Controller that is presented and dismissed multiple times of the app lifecycle, you will end up with N instances of the observed object where N is the number of times your app presented the feature.
Steps to reproduce
Using the sample project provided:
When using the "Mem leak flow" button, I expect dismissing that presentation will result in a retain cycle on ChildFeatureViewModel. So if you present the child feature 5 times, at the end in the memory debugger you'll see 5 instances of the VM alive.
When using the "Dirty fix flow" button, it shows the same child feature but once dismissed, it gets released from memory
SwiftUI Navigation version information
2.3.0
Destination operating system
iOS 17 and iOS 18
Xcode version information
Xcode 16.0.0
Swift Compiler version information
The text was updated successfully, but these errors were encountered: