【转载】UE4 <指针> UObject智能指针

705 阅读4分钟

原文地址:UE4 <指针> UObject智能指针

正文

Guoyoko/智能指针示例项目​gitee.com/guoyoko03/ue4-guoyoko_pro

说在前面:在做 GC 相关测试时,实例化的对象,不建议使用 Actor 类型(Spawn 到场景中),而是新建一个空 UObject 来进行测试(NewObject),原因见这个链接 UE4 垃圾回收系统_案例<0>

0. 测试环境

新建一个 Actor 子类 MyActor,使用蓝图继承,然后放入到关卡中.

新建一个 UObject 子类 MyObject,用智能指针指向该类型的实例化对象.

.h

声明成员变量,然后再声明 UObject 智能指针成员变量,

class SMARTPOINTERSDEMO_API AMyActor : public AActor
{
    ... // 略去无关代码
    TWeakObjectPtr<UObject> TestWeakObject;
    TStrongObjectPtr<UObject> TestStrongObject;
	
    FSoftObjectPtr TestFSoftObject; 
    TSoftObjectPtr<UObject> TestTSoftObject;
    TSoftClassPtr<UObject> TestTSoftClass;
	
    TLazyObjectPtr<UObject> TestLazyObject;
}

.cpp

BeginPlay 时,动态实例化 UObject 对象,并定义计时器,每隔一秒输出智能指针的状态。

Tick 函数中记录游戏总时间,在游戏时间达到 DestroyTime 的的时,调用智能指针的 Reset 方法,观察智能指针发生的变化. 在 Tick 函数中调用强制 GC 方法

下面的代码以 TWeakObjectPtr 为例

void AMyActor::BeginPlay()
{
    TestWeakObject = NewObject<UMyObject>();
    UE_LOG(SPLog, Display, TEXT("NewObject %s address is  %p"), *TestWeakObject->GetName(),     TestWeakObject.Get());
    auto WeakObjectPtrFunc = [this]() {
	if (TestWeakObject.Get())
	{
		UE_LOG(SPLog, Warning, TEXT("TestWeakObject address is  %p"), TestWeakObject.Get());
	}
	else if (TestWeakObject.IsStale())
	{
		UE_LOG(SPLog, Warning, TEXT("TestWeakObject address is  %p"), TestWeakObject.Get());
	}
	else
	{
		UE_LOG(SPLog, Error, TEXT("TestWeakObject address is  %p"), TestWeakObject.Get());
	}
    };
    FTimerHandle TimerHandle;
    GetWorld()->GetTimerManager().SetTimer(TimerHandle, WeakObjectPtrFunc, 1.0f, true);
}

void AMyActor::Tick(float DeltaTime)
{
    TotalTime += DeltaTime;
    if (TotalTime >= DestoryTime)
    {
        TestWeakObject.Reset();
    }
    // 强制 GC
    GEngine->ForceGarbageCollection(true);
}

1. TWeakObjectPtr

弱智能指针 所指向的对象销毁后,弱智能指针 就指向了 nullptr

调用 弱智能指针Reset 方法,IsStale() 则返回 false,在没有重置或没有被重新分配,返回的是 true.

2. TStrongObjectPtr

关键代码替换

TestStrongObject = TStrongObjectPtr(NewObject<UMyObject>());
UE_LOG(SPLog, Display, TEXT("NewObject %s address is  %p"), *TestStrongObject->GetName(), TestStrongObject.Get());
auto StrongObjectPtrFunc = [this]() {
	if (TestStrongObject) {
		UE_LOG(SPLog, Warning, TEXT("TestStrongObject address is  %p"), TestStrongObject.Get());
	}
	else {
		UE_LOG(SPLog, Error, TEXT("TestStrongObject address is  %p"), TestStrongObject.Get());
	}
};
  • 阻止指向的对象被 GC

  • 调用 强智能指针Reset 方法,原指向的对象被 GC

下面的智能指针,测试代码和上面大同小异,都是替换关键代码,观察智能指针的变化,代码部分就省略了.

3. FSoftObjectPtr

4. TSoftObjectPtr & TSoftClassPtr

5. TLazyObjectPtr

6. FSoftObjectPath & FSoftClassPath

7. UE C++ 与 蓝图变量类型的对应关系

  • 图中引用对象的名字和类型一致

  • 图中引用对象的名字和类型一致

8. 总结

硬对象指针:

所引用的 UObject 的持有者,除非指向该对象的所有硬指针都变为空,或者在对象上专门调用了 Destroy,此时将解析为 nullptr阻止对象被 GC

软对象指针:

对象或资产的路径的字符串表示,需要时显式被加载。 它在内部存储了一个附加的弱指针,以在对象被查询和找到后(在非编辑器构建中)缓存它。不会阻止对象被 GC

弱对象指针:

被创建或设置为指向已经使用其 GUObjectArray 索引实例化的现有 UObject。 指针不需要是 UPROPERTY 就可以知道它指向的对象是否已被垃圾回收。不会阻止对象被 GC


本文只是从使用的角度,展示了各个 UObject 智能指针,对源码的分析也有很多的博文,知其然知其所以然 .

  • FSoftObjectPtr: 不推荐使用。TSoftObjectPtr 的非模板化和非 BP 公开版本
  • TSoftObjectPtr: 推荐使用。用于引用可能会或可能不会通过其路径加载的对象。即使没有加载,也可以指向其他关卡中的 Actor。当指向资产(如网格)以临时加载它们时,可以与异步加载函数一起使用
  • TSoftClassPtr: 推荐使用。用于引用可能加载或未加载的类或蓝图类型。加载后,它将为您提供一个可以从中创建实例的类类型。可以与异步加载函数一起使用来加载它们
  • TWeakObjectPtr: 推荐使用用于引用已经实例化的对象。如果引用的对象被销毁或垃圾​​收集,将解析为 nullptr
  • TStrongObjectPtr: 不推荐使用。引用的对象,强制不会被GC
  • FSoftObjectPath: 不推荐使用。由上面的指针类型在内部使用,很慢,因为它不缓存结果
  • FSoftClassPath: 不推荐使用。与 FSoftObjectPath 相同,但有一些与加载类相关的辅助函数

一句话:推荐使用带 T 的,也就是模板 弱对象智能指针和软对象智能指针

参考文章