I am very new to Unreal Engine, and I got confused about memory management and the Garbage Collector. I read the wiki and some online documentation, and checked the “Tower Defense”/“Strategy Game” code. Asuming that all references are kept in UPROPERTY()-warded pointers:
Does Unreal Engine Garbage Collector detect circular references by itself? That is: does it detect unconnected components of the object graph and deletes them?
Or do I have to use TWeakObjectPtr to break reference cycles?
An example of this:
In the “Tower Defense” sample, I checked that AStrategyGameController contains several UStrategyAIAction, and that the latter caches a weak reference to the former in UStrategyAIAction::SetController. Is this mandatory? Or is it an aid to the Garbage Collector performance?
(My use case is a strategy game where a “board” can contain “tiles”, and the tiles keep a reference to the board to delegate some functionality clicked. Should this be a weak reference?)
Detecting circular references would quickly become extremely complex, with a far greater cost than simple reference counting. (What if you have a circular reference through multiple intermediate classes? Do you start maintaining a graph of all object relations and do cycle detection whenever it is modified? etc.)
In pretty much all cases, you can avoid this sort of circular referencing by making the dependent object passive and expose backreferences through delegates instead. Then your controller just registers to those delegates, instead of the dependent object directly calling the controller. But that’s not really necessary outside of UI code, unless you really want to avoid tight coupling.
Well, you are right about that. But since I am the sole programmer and haven´t got much time I am taking the quick and sloppy way for my Board-Tiles coupling.
Uh, you lost me there, I haven´t learned much about Unreal’s UI yet. Why is it necessary for this case? (mind: I worked a lot with objective-C, where delegates and target-action are a big thing in UI; I just never thought of it as something mandatory, but helpful instead)
I did say it’s not really necessary. Decoupling classes through delegates and Observer-like patterns just makes code easier to follow by reducing the amount of classes you need to look at when following data relationships.
In the case of UI, it has the added advantage that properly decoupled widgets are much easier to reuse across a UI than widgets that backreference their parent.
As an example, our intern worked on some Slate UI widgets for an inventory. Every widget obtained its data by keeping a (weak) pointer to their parent… a Character subclass. Later on, I wanted to use these inventory widgets to display the contents of a container in the world, which is definitely not a Character. I simply couldn’t use any of the widgets in that hierarchy because they all depended on a PlayerController and its pawn owner being passed around. When refactoring this code, I instead made every inventory widget fully passive with a set of delegates, then introduced presenter classes which are in charge of mediating connection between character inventories, container inventories by registering with delegates and otherwise providing data without the inventory widgets ever being aware of what is talking to them.
If you’re working on a one programmer project, then making code easier to follow isn’t as important since you already know how it works. And if you know you’re not reusing the widget in different contexts, UI reuse also isn’t as important. It’s just something to keep in mind when either of these situations does not apply.
I was confused about the “unnecessary outside of UI code” part, I got scared that I was missing on a strong demand of the UI system. Anyways, I am indeed using abstraction of data sources for UI elements that need some kind of reuse. Flying solo makes reuse and maintainability all the more crucial.