Garbage collector removes elements from TArray of UStruct

I made 2 simple struct using the USTRUCT() macro. One basically holds an array of the other; that’s because I wanted to make a 2-dimensional array of struct and Unreal wouldn’t let me:



USTRUCT(BlueprintType)
struct FAssemblyMove
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Assembly Move Struct")
    int32 MoveNumber;

    FAssemblyMove(int32 pMoveNumber)
        : MoveNumber(pMoveNumber)
    {

    }
};

USTRUCT(BlueprintType)
struct FStep
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Assembly Step Struct")
    TArray<FAssemblyMove> MoveList;

    FStep()
    {

    }
};


Now, in the class file in which these structs are declared I have this (an array and a method to add an element to it):



UCLASS()
class FPSTEST2_API UAssemblyInfoManager : public UObject
{
    GENERATED_UCLASS_BODY()
public:
    FAssemblyMove AddMove();
private:
    UPROPERTY()
    TArray<FStep> mStepsList;
};


The code that adds the element is pretty straightforward:



FAssemblyMove UAssemblyInfoManager::AddMove()
{
    // I retrieve the last element of the array
    FStep step = mStepsList[mStepsList.Num() - 1];

    FAssemblyMove newMove;
    newMove.MoveNumber = step.MoveList.Num() + 1;

    step.MoveList.Insert(newMove, step.MoveList.Num());

    return newMove;
}


Now, as a test, I add an element to “mStepsList”; then I call AddMove() to add one element to the inner array that it contains. This seems to work, but “soon” (not sure when) the MoveList array contained inside the FStep I retrieve from mStepsList gets deleted. I tried several times, but every element I add always get deleted soon.

I’m thinking is a garbage collection problem, but as far as I know I’m playing it “safe” by using UPROPERTY() on all these variables. What am I doing wrong then?

Weird, does UAssemblyInfoManager is referenced by something with UPROPERTY field ?

Yes it is. Also, the object that uses UAssemblyInfoManager has also the UPROPERTY() field, and so does every other object “in the path” from the player pawn class to the code I posted above. I really don’t get what is wrong…

EDIT: one other thing is worth mentioning. When I add a FStep to the mStepsList array using this piece of code (I didn’t show it because I didn’t think it was relevant)



void UAssemblyInfoManager::AddStepAtIndex(int32 pStepIndex)
{
    FStep newStep;
    mStepsList.Insert(newStep, pStepIndex);
}


it works as expected, meaning that the FStep I create and add here isn’t garbage collected.

I would think the struct in this case isn’t GC’d due to the fact that you aren’t dynamically allocating it, it’s just a simple pass by value. More likely, I think your UAssemblyInfoManager is getting cleaned up somewhere along the way and you just aren’t noticing it till you try to do some action upon it (i.e. add a value to that array member).

A quick way to test this is just to create a simple destructor for both the class and the struct and toss a break point in there - see what is getting hit when.


FStep step = mStepsList[mStepsList.Num() - 1];

Needs to be


FStep &step = mStepsList[mStepsList.Num() - 1];

You’re making a new copy of the FStep object and modifying the copy, so your items were never added in the first place.

And also, GC only ever operates on UObjects, structs will never be GC’d and need to be manually managed.

1 Like

Man, you’re right! So stupid! I don’t know how I missed that.

Thank you so much! Also thanks to anyone who tried to help me :slight_smile: