UObject* lifetime in blueprints

As I understand it, if you create a UObject on the heap using NewObject, it is [registered with UE4’s Garbage Collection system][1], so when all pointers are out of scope, it will be deleted from the heap automatically unless it is registered as a UPROPERTY. My question is, how does this relate to a UObject heap pointer that I return to a blueprint?

I’m looking to create a static library function that creates a UObject subobject on the heap and returns the pointer to a blueprint node. (There’s various applications for this pattern, for example I’d like to use it to return a custom path object to BP after some pathfinding C++ is run. It needs to return a new path object every time it’s called, so it can’t just return a pointer to some member UPROPERTY.)

Example code:

TestObject.h

//...
UCLASS()
class PATHFINDINGTEST_API UTestObject : public UObject
{
    GENERATED_BODY()
	// Insert UPROPERTYs and UFUNCTIONs etc. here...
};

PathfindingCodeLibrary.h

//...
UCLASS()
class PATHFINDINGTEST_API UPathfindingCodeLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintPure, meta = (FriendlyName = "Get me a test object pls"))
		static UTestObject* GetTestObject(/* arguments... */);
};

PathfindingCodeLibrary.cpp

//...
UTestObject* UPathfindingCodeLibrary::GetTestObject(/* arguments... */)
{
    // Registered for Garbage Collection
    UTestObject* returnvalue = NewObject<UTestObject>();
    // Populate returnvalue here...
    return returnvalue;
}

And when all compiled, the function can be called from a blueprint:

So my question really is: what is the lifetime of the object in the above picture? Can I safely use the result from this BP node, or could the garbage collection attempt to delete it while it’s still in scope within BP, since it’s not registered as a UPROPERTY and there are no code references to it?

Also, assuming it is safe to use, will it be garbage collected safely once the node result is out of scope, or will it remain on the heap?

Thanks!

Interesting question, from my experience this object will be gc’ed and I would not rely on it state. Of course operations on this object may fit between GC ticks;) and everything may work fine, but it is dangerous.

Good question! My assumption would be that the blueprint nodes themselves would internally keep a reference to the object, although it would be up to you to eventually assign it to a UPROPERTY somewhere for it to remain alive once it went out of BP execution scope. Interested to hear an answer on this one.

Just put a 2+ minute delay between creation of this object and some operation on this object, GC by default is running once per 1 minute.

@Pierdek My initial tests appear to be safe, though as you point out this could just be because all operations on the result take place between GC ticks. Obviously that is not safe in the long run and should definitely be avoided!

I wonder if it’s safe to assign the reference to a BP property immediately after it’s generated, even if it’s not safe in general, or whether a GC tick could happen even between these events. (See image)

30974-uobjectpointertest2.png

Ok, I did two tests (in UE4.6.1). First, I generated an object using the above function then, without setting its value to a BP variable, queried it every minute for 3 minutes. It was still live throughout. This seems to confirm the first part of my question, that being referenced in blueprint does extend UObject lifetime*.

Secondly, I overrode the BeginDestroy function in UTestObject such that it prints a log message when it is destroyed. I then used the generation function within a blueprint function, ensuring that the last reference to the object was out of scope once control flow had left the function. The destruction message was successfully displayed in tests. This seems to confirm the second part of my question, that the Garbage Collector still counts the references properly in blueprint and will destroy a UObject once all pointers are out of scope.

I hope this helps anyone else in the same position as me. I would really appreciate a more complete answer from someone with a fuller understanding of the GC and how it interacts with BP. (I’ve answered the question, but I still don’t know exactly why it all works.)

Excellent question and answer. I’d really like to see Epic confirm this too.

Thanks,
-X