So about PIE. In a standalone build, your GameInstance gets instantiated once when your game launches, and is destroyed just before it shuts down. So usually we would drop the singleton-like getter, because it wastes performance on a validity check that will always return true. In that case, you’d use NewObject() once when the GameInstance itself is created (e.g. in OnStart()).
In PIE this check can return false some times, which is a pain. So I use this more complex getter to make sure it’s always valid, if I need to access the SaveGameManager. (In my GameMode, that is not necessary, because its lifetime doesn’t have this different behavior in PIE.)
Technically, you can solve this problem even more elegant by using the preprocessor. You could use the above code purely inside the editor, and use a single-time instantiation and direct access without redundant validity checks by using conditional compilation (#if UE_EDITOR). In my case, the SaveGameManager is accessed so extremely rarely, that I don’t care whether I do unneeded validity checks. For something that is accessed each frame, that may be worth considering (it’s only a handful lines of code).
You can still find the old threads where people asked about how to deal with singletons/global objects cleanly without getting GC problems. The GameInstance was added (relatively) recently by popular demand. The way Epic built it into the engine, it makes two guarantees as far as your game’s logic is concerned. The engine guarantees that the GameInstance is 1. always valid and 2. unique (PIE notwithstanding). So, by extension, everything you put in there is 1. always available and 2. exists only once.