keywords: [UE4]AddToRoot()要与RemoveFromRoot成对出现

如果对某个UObject执行了AddToRoot(),那么需要在该UObject所属的上级UOject销毁前,执行RemoveFromRoot(),否则当程序退出时,会出现崩溃,崩溃堆栈:

Assertion failed: !IsRooted() [File:d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\coreuobject\public\uobject\UObjectBaseUtility.h] [Line: 135] 

UE4Editor_Core!FDebug::AssertFailed() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\core\private\misc\outputdevice.cpp:421]
UE4Editor_UnrealEd!<lambda_26419e543909ee92ebfb672b1e9c08dc>::operator()() [d:\build\++ue4+release-4.13+compile\sync\engine\source\editor\unrealed\private\playlevel.cpp:384]
UE4Editor_CoreUObject!ForEachObjectWithOuter() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\coreuobject\private\uobject\uobjecthash.cpp:678]
UE4Editor_UnrealEd!UEditorEngine::EndPlayMap() [d:\build\++ue4+release-4.13+compile\sync\engine\source\editor\unrealed\private\playlevel.cpp:380]
UE4Editor_UnrealEd!UEditorEngine::Tick() [d:\build\++ue4+release-4.13+compile\sync\engine\source\editor\unrealed\private\editorengine.cpp:1653]
UE4Editor_UnrealEd!UUnrealEdEngine::Tick() [d:\build\++ue4+release-4.13+compile\sync\engine\source\editor\unrealed\private\unrealedengine.cpp:371]
UE4Editor!FEngineLoop::Tick() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\launch\private\launchengineloop.cpp:2834]
UE4Editor!GuardedMain() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\launch\private\launch.cpp:156]
UE4Editor!GuardedMainWrapper() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:126]
UE4Editor!WinMain() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:202]
UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:264]
kernel32
ntdll

执行RemoveFromRoot()不要放在Actor自己对象的Actor::BeginDestroy()函数内(override 父类BeginDestroy()函数),即使是在Super::BeginDestroy()执行之前。否则退出游戏时(包括PIE模式(编辑器内运行游戏)下退出游戏)仍会崩溃。

override 父类BeginDestroy()函数并在其内部执行RemoveFromRoot()导致的崩溃堆栈:

[Inline Frame] UE4Editor-Engine.dll!UObjectBaseUtility::MarkPendingKill() Line 150    C++
[Inline Frame] UE4Editor-Engine.dll!UWorld::MarkObjectsPendingKill::__l2::<lambda_def76fd07ced75fbbe91915eb5003628>::operator()(UObject * Object) Line 1500    C++
[Inline Frame] UE4Editor-Engine.dll!Invoke(UWorld::MarkObjectsPendingKill::__l2::<lambda_def76fd07ced75fbbe91915eb5003628> &) Line 45    C++
UE4Editor-Engine.dll!UE4Function_Private::TFunctionRefCaller<<lambda_def76fd07ced75fbbe91915eb5003628>,void __cdecl(UObject * __ptr64)>::Call(void * Obj, UObject * & <Params_0>) Line 251    C++
[Inline Frame] UE4Editor-CoreUObject.dll!UE4Function_Private::TFunctionRefBase<TFunctionRef<void __cdecl(UObject *)>,void __cdecl(UObject *)>::operator()(UObject * <Params_0>) Line 290    C++
UE4Editor-CoreUObject.dll!ForEachObjectWithOuter(const UObjectBase * Outer, TFunctionRef<void __cdecl(UObject *)> Operation, bool bIncludeNestedObjects, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) Line 711    C++
UE4Editor-Engine.dll!UWorld::MarkObjectsPendingKill() Line 1503    C++
UE4Editor-UnrealEd.dll!UEditorEngine::EndPlayMap() Line 415    C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1957    C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 403    C++
UE4Editor.exe!FEngineLoop::Tick() Line 3495    C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62    C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 166    C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 209    C++

崩溃的函数位置:

FORCEINLINE void MarkPendingKill()
{
    check(!IsRooted());
    GUObjectArray.IndexToObject(InternalIndex)->SetPendingKill();
}

建议重写AActor::EndPlay()并在其内部执行RemoveFromRoot。这样PIE模式下退出游戏或者Game模式退出游戏都不会崩溃

void AActor::EndPlay(const EEndPlayReason::Type EndPlayReason)

另外,在GameMode和GameInstance的BeginDestroy()中执行是不行的,因为垃圾回收过程发生在BeginDestroy()执行之前。
GameInstance::Shutdown()中执行RemoveFromRoot也可以。