Do delegates automatically get removed when an actor is destroyed?

I try to call .RemoveDynamic() in the destructor and it is crashing my program sometimes…

I am calling OnComponentBeginOverlap.AddDynamic(this, &ThisActor::Function).

The class in the parenthesis gets destroyed and I call OnComponentBeginOverlap.AddDynamic() on a new class that is spawned.

I don’t want the component that is calling the delegate function to have a bunch of useless delegates on it.

Instead of handling AddDynamic and RemoveDynamic in the constructor/destructor, have you tried handling them in the BeginPlay() and EndPlay() functions? If you want to be safe and ensure that the delegates are bound as soon as possible, you could call Super::BeginPlay() and Super::EndPlay() after the delegates are added/removed.

To answer the title question, I do not believe so. I think all delegates must manually be removed. Ideally EndPlay() seems like the best place to handle it, from my experience (That said, UE4-built-in delegates might handle their cleanup differently).

I forgot that Endplay() was a thing! This is perfect, thank you!!!

For implementation details on delegates, see Engine\Source\Runtime\Core\Public\UObject\ScriptDelegates.h.

Multicast dynamic delegates store an array of script delegates. The concern with not calling RemoveDynamic is that we’ll be adding things to this array and never removing them.

If we look at the definitions for AddDynamic, AddUniqueDynamic, and RemoveDynamic, we see that we call the function CompactInvocationList() before each one, which is defined as follows:

void CompactInvocationList() const
	{
		InvocationList.RemoveAllSwap([](const TScriptDelegate<TWeakPtr>& Delegate){
			return Delegate.IsCompactable();
		});
	}

Delegate.IsCompactable() is defined as:

inline bool IsCompactable() const
	{
		return FunctionName == NAME_None || !Object.Get(true);
	}

Basically, every time we make any changes to the list of delegates, we delete any delegates that reference a listener that’s been destroyed.

So, in answer to your question:

Yes if you add or remove delegates after the actor is destroyed. No if you don’t.

Or in other words, usually yes.

In some probably very rare cases you may want to manually manage delegates, and tight memory management is probably good practice.

3 Likes

For implementation details on delegates, see Engine\Source\Runtime\Core\Public\UObject\ScriptDelegates.h.

Multicast dynamic delegates store an array of script delegates. The concern with not calling RemoveDynamic is that we’ll be adding things to this array and never removing them.

If we look at the definitions for AddDynamic, AddUniqueDynamic, and RemoveDynamic, we see that we call the function CompactInvocationList() before each one, which is defined as follows:

void CompactInvocationList() const
	{
		InvocationList.RemoveAllSwap([](const TScriptDelegate<TWeakPtr>& Delegate){
			return Delegate.IsCompactable();
		});
	}

Delegate.IsCompactable() is defined as:

inline bool IsCompactable() const
	{
		return FunctionName == NAME_None || !Object.Get(true);
	}

Basically, every time we make any changes to the list of delegates, we delete any delegates that reference a listener that’s been destroyed.

So, in answer to your question:

Yes if you add or remove delegates after the actor is destroyed. No if you don’t.

Or in other words, usually yes.

In some probably very rare cases you may want to manually manage delegates, and tight memory management is probably good practice.

Awesome! Thank you so much for the detailed answer!!

1 Like