Chris Ashworth

Mountain

Today's tip relates to another common, but particularly insidious error in Unreal code that can end up costing huge amounts of frustration and lost time.

Consider the code below:

UCLASS()
class ATestActor : public AActor
{
  UPROPERTY()
  FMyStruct Data1;

  FMyStruct Data2;
}

USTRUCT()
struct FMyStruct
{
  UPROPERTY()
  UMyObject* Object
}

You have a struct which contains a UObject pointer (yes, this should be TObjectPtr<> in UE5), and an actor that has two member instances of that struct, however, only one of them is marked UProperty.

Let's say you spawn in this actor in your game, and you create a UObject, and Data1::Object and Data2::Object to point to it. Then, you destroy the UObject. What happens?

Answer : The engine will zero out Data1::Object, but Data2::Object will remain as a dangling pointer. 

Why? Because UProperties need to exist in an unbroken chain from the top-level object (usually the world). Because ATestActor::Data2 doesn't have a UPROPERTY() specifier, that chain is broken, and Data2::Object does not exist as a property in the engine, so it can't deal with nulling it when the object is destroyed.

This leads to all sorts of potential issues. The worst kind of non-obvious, intermittent, inconsistent, issues with object lifetimes, cooking, unexpected garbage collection, and potential memory stomps. 

Best of all, you won't get warned that this is a problem! So make sure you keep an eye on your property chains, and if you have any weird intermittent property issues, double check them.