I want to dereference class B. I was surprised to learn that setting b to nil was not enough to make this class go out of scope (cleaned up by ARC). Since that didn’t work I’ve shifted my focus to see which views may still be holding a reference to this class.
I’ve got a few questions here:
Are there any semantics to be especially aware of, i.e. are property wrappers @Bindable and @Stateetc equal in reference retention to their ordinary counterparts - varlet
If a view takes B as a nullable parameter, when would b ever be de-referenced? When the view disappears and/or when the parameter becomes nil
Does Xcode / Instruments offer any ways to gain insight to what may be holding the references here?
I have debugged some issues like this in the past so this response is based on my personal experiences.
In my experience, you may see multiple retains of an object due to its use in a SwiftUI view hierarchy, but you can generally treat them all as one "group" of retains that will be released all together.
By de-referenced I assume you mean released. Optional (nullable) parameters are the same as non-optional parameters when it comes to reference counting.
The only thing that really matters for your question is what happens in the body of the parent view that passes the object as a parameter:
When the body is updated, if nil is now passed to that subview instead of the object, that subview hierarchy's references to the object will be released.
Additionally, if the view is no longer visible because of a conditional statement in the parent body, the references to the object in that former subview hierarchy will be released.
The best way to do this is to use View Memory Graph in Xcode. It is in the debug tray above the console when debugging an app, with an icon that looks like this:
When the memory graph is loaded, you can filter for the class of the object you care about in the navigator sidebar, and then see all the live instances of that object in memory. When selecting an instance, the graph will show all the strong and weak references to that object.
References to objects stored within SwiftUI can be sometimes quite opaque, but it's generally clear that they are within SwiftUI.
Another piece of advice that I will offer is that, in my own experience, objects sometimes end up staying alive longer than you'd expect because they end up in NSAutoreleasePools that belong to the runloop or other parts of the UI frameworks. The cadence at which these pools get released is a private implementation detail of the frameworks, and I have seen objects stay alive for several seconds or more after all "real" references to them are gone.
In general, use the Memory Graph tool in Xcode to see if there are any "real" references to your object. If you have determined that there are no reference cycles or other kinds of standard memory leaks with your object, and the only references are in autorelease pools, at that point I would assume that there's nothing more you can do, and I would only start to worry again if your object doesn't eventually get deinit after several minutes of using your app normally.