如何不收集 UObject 垃圾的主题已在数百篇帖子中重新讨论,但我发现似乎没有一个解决方案适合我的情况。
这里是 MVP 项目存储库:https://github.com/eSqadron/ObjectDisappearingFromTArrayMVP
我在游戏模式中有一些自定义
UMyObject : UObject
和这些对象的数组 TArray<class UMyObject*> MyObjects;
:AMvpGameModeBase
。数组是在游戏模式构造函数中创建的。数组由 AMvpGameModeBase::GetNextObject()
和 AMvpPlayerController::BeginPlay()
引用。 Begin Play 创建小部件,它们是 UMyObject
类的组成部分(尽管如果没有它,它也可能以同样的方式崩溃)。
现在,我的 MVP 项目显示按钮,每次奇怪的单击都会从 TArray 中获取下一个 UMyObject 并在屏幕上显示相关的小部件。每次单击都会隐藏现有的小部件。如果一切都按我的预期进行,那么在 10 个屏幕之后我们应该会收到一个错误,因为我们用完了数组元素。如果我们点击按钮的速度足够快,就会发生这样的情况。然而,如果我们多花一点时间(大约一分钟左右)来给 GC 运行时间,一切都会突然变成空。对象名称正在消失,(Null 而不是 My_Object_XX)无法设置小部件文本,因为访问冲突等。
在这里你可以看到
UMyObject
清楚地收集了垃圾:
我已经尝试了一些解决方案,但似乎都不起作用:
解决方案1:
将
TArray<class UMyObject*> MyObjects;
设置为 UPROPERTY()
。在这种情况下,MyObjects
数组填充在游戏模式构造函数中,但是当我在 PlayerController BeginPlay 中引用该数组时,一切都为空:
为什么会发生这种情况?当在构造函数而不是 Begin Play 中创建对象时,虚幻反射系统会做一些奇怪的事情吗?
解决方案2: 通过游戏模式构造函数中的
_newObject->AddToRoot();
将 MyObjects 添加到根目录。这是部分修复,因为对象本身似乎不再被垃圾收集,但作为对象一部分的小部件(仅在这个 MVP 中是MyWidget
)仍然是。
将
MyWidget
添加到根 (obj->MyWidget->AddToRoot();
) 来解决这个问题,这完全是一场灾难。离开游戏而不从根目录中删除所有小部件会导致单击编辑器中的停止按钮时崩溃。另外,我只能在调试模式下运行游戏,在没有调试模式的情况下从 VS 中单击编辑器中的“播放”会导致崩溃。但第二个问题我只在我的实际项目中观察到,而不是这个 MVP 应用程序。
解决方案3: 使用
UPROPERTY()
分别创建所有对象,然后将它们添加到 TArray MyObjects
。
这和基本行为之间没有明显的区别,物体仍然消失。
解决方案4: 仅将标志设置为 RF_MarkAsRootSet,而不是 AddToRoot()。
此行为与基本行为之间也没有明显的差异,物体仍在消失。
我还尝试了这些解决方案的一些随机组合。我觉得第一个解决方案是最合适和“干净”的,但我不知道这些空值来自哪里。关于 UE Reflection System 及其垃圾收集器,我还有很多东西需要学习。我非常感谢任何帮助,因为我已经在这个问题上待了一个多星期了。
我想我已经明白了,解决方案 1 和 2 的结合:
_newObject->AddToRoot();
在游戏模式下添加到rootUPROPERTY()
至 class UMyWidget* MyWidget;
但这是反复试验,我不知道它为什么有效,如果有人知道它到底是如何工作的以及为什么这个解决方案有效,而其他人正在引起问题,我很想听到一些详细的解释。