Enforce TObjectPtr! (And how to avoid pitfalls)
Updated for UE 5.8: clarified the default UHT behavior, and that the mutable escape hatch is
MutableView(the old mutableToRawPtrArrayUnsafestyle helpers are deprecated).
Since UE5, TObjectPtr is the way for reflected classes and structs to hold UObject pointers as members.
Pre-UE5 you did
UPROPERTY(EditAnywhere)
UMyObject* MyObject;
Now you should do:
UPROPERTY(EditAnywhere)
TObjectPtr<UMyObject> MyObject;
Raw pointer UPROPERTY members still compile, because UnrealHeaderTool defaults to allowing them silently. To enforce TObjectPtr, I highly recommend placing the following in your Target.cs file (we normally just do it in MyGameEditor.Target.cs).
if (!bBuildAllModules)
{
NativePointerMemberBehaviorOverride = PointerMemberBehavior.Disallow;
}
NativePointerMemberBehaviorOverride applies to engine, engine plugin and non-engine modules alike. UHT will now error on a native pointer member in a UCLASS or USTRUCT, and tell you to use TObjectPtr. The bBuildAllModules check just keeps this off for the build-everything case, where you may compile modules you don’t own. Other values are AllowSilently (default) and AllowAndLog.
TObjectPtr is meant for members only. Function parameters, return values and locals should stay raw pointers. That is where you can hit friction with engine or older APIs that take raw pointer containers, but Epic gave us helpers.
ObjectPtrDecay
Use this when you need const access to the underlying storage of a TObjectPtr (or a container of them, including nested TArray, TSet and TMap), for example to pass it to a function that takes const TArray<AActor*>&.
UPROPERTY(Transient)
TArray<TObjectPtr<AActor>> Actors;
void SomeFunc(const TArray<AActor*>& InActors);
void MyFunc()
{
SomeFunc(ObjectPtrDecay(Actors));
}
ObjectPtrDecay returns a const reference, so it does not copy. Assigning the result to a non-reference variable, as in TArray<AActor*> Selected = ObjectPtrDecay(Actors);, does make a copy of the array.
ObjectPtrWrap
The inverse of the above: it views a TArray<UObject*> as a TArray<TObjectPtr<UObject>> (also works for TArrayView, TSet and TMap).
MutableView
Use this when a function needs to write to the container through raw pointers. It gives temporary mutable access to the TObjectPtr storage, and the pointers are brought back in sync when the view goes out of scope.
void GetAllComponents(TArray<UActorComponent*>& OutComponents);
TArray<TObjectPtr<UActorComponent>> Components;
GetAllComponents(MutableView(Components));
Keep the view as a temporary or a named scoped variable. Don’t bind a raw reference to it and keep it around; the header comment calls that out as a hard-to-find bug.
Hopefully this helps, just a small random bit of information!