FString in TArray of pointers changes its value to garbage

Hey all,

I’m struggling with one issue which I tried to approach from several POVs and finally gave up. I’m trying to generate child widgets in a sequence using simple iteration:

for (UConstructionBuilding* building : GameInstanceUtils::GetGameInstance()->EnergyBuildings)
{
    UConstructionMenuItem* widget = CreateWidget<UConstructionMenuItem>(this, this->ConstructionMenuItemClass);
    widget->SetData(building, this->Planet);
    
    this->ConstructionMenuItems.Add(widget);
    energyBuildingsPanel->AddChild(widget);
}

EnergyBuildings is a public field populated inside my game instance constructor (removed the rest of the items as they are populated in the same manner):

UConstructionBuilding* coalPowerStation = NewObject<UConstructionBuilding>();

coalPowerStation->SetBuildingData("Coal-fired power station", 4, 15, 20, 1, 2.5, 2, ResourceTypeEnum::COAL);

this->EnergyBuildings.Add(coalPowerStation);

The problem is that when I run my game, instead of the passed string I see some garbage value while the rest is completely fine:
image
It seems like I tried to read invalid memory address and got some random value, but I cannot pinpoint the issue. Tried to debug the code and I see, that when I read TArray inside the constructor, all the data is initialized correctly, the problem appears if I try to read it later when the game runs. Tried to add additional TArray of FStrings on game instance class, works completely fine.

Should I handle TArray<UConstructionBuilding*> in different manner as TArray of value types?

EDIT: I see, that if I move the whole array initialization to a function and just create all the elements there (so the whole array is created with each function call), initially it looks okay, but after several seconds the names are again replaced with garbage starting with the very beginning like something’s started to overwrite the memory used by the string.

Is your TArray preceded by the UPROPERTY macro? sounds like your objects are being garbage collected.

Hey, thanks for answering. I double checked the setup and thinks looks like that:

  • UConstructionBuilding derives from UObject, has UCLASS(BlueprintType) macro, but none of the fields use UPROPERTY, only UFUNCTION for getters
  • TArray<UConstructionBuilding*> in my game instance doesn’t use any of UE macros, it’s just a common field and as I said, even if I move array generation to a function called individually, still FString seems to be garbage collected at some point
  • Created an empty project, added a custom game instance class with TArray of custom types and for now couldn’t reproduce the issue so I assume it’s something specific to my main project

Does UE have a mechanism of removing a specific type from garbage collecting?

If your GameInstance array is the only persistent reference to those objects you must have a UPROPERTY macro before it or else those objects will be garbage collected. The only way to exclude a type from garbage collection is to not derive from UObject. But you’d have to then guarantee that type (and any types it uses) are also not responsible for keeping a UObject from being garbage collected.

I’m not sure what your second test entailed exactly. If by “custom type” you’re referring to a struct than it wouldn’t be an issue since you wouldn’t have object pointers in the array anymore but structure instances.

There’s no real downside to those macros and usually there’s no reason to not apply them to all members of UObjects and USTRUCTS. That’s just how Unreal works. Occasionally there are member types that aren’t supported as a UPROPERTY (like TArray< TArray< > >) but there usually work arounds for those cases.

Huh, so I must say I got garbage collecting docs a little bit wrong. I thought that if an object derives from UObject and in general is treated as Unreal object, that’s enough for the engine to do all the work. In other words - I assumed, that if UObject holds references to other UObjects, you’re good with GC and other stuff.

Changed the declaration to the following:

UPROPERTY()
TArray<UConstructionBuilding*> EnergyBuildings;

Works like a gem. From now on I’ll pay more attention to those macros :slight_smile: Thank you very much, I don’t know how many hours I spent on debugging that issue!

Yeah, the docs probably assume that your references are marked up with the macros.

The garbage collector (in general) relies on reflection data to determine what objects hold references to other objects. The macros are what provides that reflection data. So no macro, no reflection and not visible to the GC.

Glad it’s working, and like I said before there’s no reason to not apply them. So you don’t have to be careful if you just apply the macro all the time. At least until Unreal Header Tool complains about a type that isn’t supported! :smiley:

Yup, now it is obvious but that’s a worthy lesson to keep an eye on all the macros and double-check that they are applied everything where it’s necessary :slight_smile:

Great that you’ve managed to fix the issue. But there is another point to take care of now.
You shouldn’t use strings for something that will be displayed on the screen. Use FText instead. Actually for anything that will be used in UI. Remember that you can’t localize strings and they are more expensive.
You can learn more about it via link: docs.unrealengine.com/4.27/en-US/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/StringHandling/

Thanks! It’s my C# background coming into play sometimes, don’t know why I assumed FText is not a better choice there. Appreciate the link :slight_smile:

@MagForceSeven
If your GameInstance array is the only persistent reference to those objects you must have a UPROPERTY macro before it or else those objects will be garbage collected.

That’s not true.
You just need to specify outer when creating the object and you don’t need any macros.

UConstructionBuilding* coalPowerStation = NewObject<UConstructionBuilding>(this); 

Now, coalPowerStation is reachable from UGameInstance, so as long GameInstance exists, coalPowerPlant instance exists too.

I’m pretty sure that’s not true, but I’m not 100% on that. I’ve created plenty of things with outer’s the way you suggest and always seem to be able to free the object by only clearing out the member I have that references them. If what you were suggesting were true, I would also have to change the outer because the outer would keep the object alive.

From Epic’s own documentation: " One practical implication here is that you typically need to maintain a UPROPERTY reference to any Object you wish to keep alive, or store a pointer to it in a TArray or other Unreal Engine container class." here
They specifically call out Actors (since they’re referenced by the world) and Actor Components (referenced by an Actor), but not arbitrary outers. Do objects know the things they’re outers of? I’ve never seen a member on object that does that. At best you could keep your outer alive, but your outer can’t keep you alive.

Thanks for your response. Unfortunately I tried that - specifying outer didn’t prevent GC of those objects.

1 Like

Yes, @MagForceSeven is right. Just setting outer is not sufficient.
This requires UPROPERTY or AddToRoot. (or AddReferencedObjects? I haven’t checked how it works yet).

That’s true, AddToRoot and AddReferencedObjects are advanced options for GC management. But they’re usually for circumstances where a UPROPERTY reference isn’t available (objects not really owned by anything or references stored in data that can’t be properties like nested containers).