UE4 static 变量 GC 导致闪退问题
最近开发一个 UMG 插件,插件里用到了静态变量,在 UE4 编辑器里运行没有啥问题,然后打了手机包iOS后,运行一段时间后,闪退了,闪退点就是我写的代码里中访问全局变量的时候,使用 UnrealVS 插件在 VS2019 下开启 Window 模式,也出现了类似的闪退问题:
UnrealVS 参数
“E:\xxx.uproject” -game -windowed -log -verbose
闪退代码处:
// .h 文件
UCLASS()
class UMGEXT_API UExtTextBlock : public UTextBlock
{
static UDataTable* ExtTextStyleSet;
}
// .cpp 文件
UDataTable* UExtTextBlock::ExtTextStyleSet = nullptr;
void UExtTextBlock::EnsureTextDataTable()
{
if (nullptr == ExtTextStyleSet || !ExtTextStyleSet->IsValidLowLevel())
{
ExtTextStyleSet = LoadObject<UDataTable>(nullptr, *DataPath);
ExtTextStyleSet->AddToRoot();
}
}
FTextBlockStyle UExtTextBlock::GetTextStyleByName(FName StyleName)
{
EnsureTextDataTable();
FTextBlockStyle Style = FTextBlockStyle::GetDefault();
if(nullptr == UExtTextBlock::ExtTextStyleSet)
{
return Style;
}
/// 闪退地方
auto RowMap = UExtTextBlock::ExtTextStyleSet->GetRowMap();
if(RowMap.Contains(StyleName))
{
FRichTextStyleRow* RichTextStyle = (FRichTextStyleRow*)(RowMap[StyleName]);
Style = RichTextStyle->TextStyle;
}
return Style;
}
用 VS2019 调试闪退点,查看 UExtTextBlock::ExtTextStyleSet 内存,每次都不一样,而且类型对象完全不匹配,所以联想到可能是内存空间被GC,然后之前指向的内存被其他变量占用了,于是去谷歌。
要防止对象被GC,有4种方式:
- 作为成员变量并标记为UPROPERTY();
- 创建对象后 AddToRoot() ;(退出游戏时需要RemoveFromRoot())
- FStreamableManager Load资源时,bManageActiveHandle 设置为true;
- FGCObjectScopeGuard 在指定代码区域内保持对象;
注意:
一个UObject类型的变量,即使是static,默认也会被GC掉。
知道了原因,就好解决问题了,于是做了一下修改:
/// 插件模块代码
/// 启动时: 加载创建静态变量,并且将 静态变量加到 Root 上
void FUMGExtModule::StartupModule()
{
UE_LOG(LogUMGExtModule, Display, TEXT("UMGExtModule StartupModule"));
UExtTextBlock::EnsureTextDataTable();
}
/// 模块卸载时:将静态变量从 Root 拿掉
void FUMGExtModule::ShutdownModule()
{
UE_LOG(LogUMGExtModule, Display, TEXT("UMGExtModule ShutdownModule"));
UExtTextBlock::ClearTextDataTable();
}
/// 对应类的代码
void UExtTextBlock::EnsureTextDataTable()
{
if (nullptr == ExtTextStyleSet || !ExtTextStyleSet->IsValidLowLevel())
{
ExtTextStyleSet = LoadObject<UDataTable>(nullptr, *DataPath);
/// AddToRoot : 防止被 GC
ExtTextStyleSet->AddToRoot();
}
}
void UExtTextBlock::ClearTextDataTable()
{
if (nullptr != ExtTextStyleSet)
{
/// RemoveFromRoot : 可以 GC 了
ExtTextStyleSet->RemoveFromRoot();
}
}