(UE4 4.20 )UE4的GC(垃圾回收)编程规范

2023-10-20 23:20

本文主要是介绍(UE4 4.20 )UE4的GC(垃圾回收)编程规范,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

UObject系统的GC

UPROPERTY 引用

当我们在一个UObject类声明各种继承UObject的 变量时,得加UPROPERTY(), 这个可以让UE4帮我们自动管理UObject的垃圾回收。UPROPERTY不仅仅用于反射变量到编辑器上编辑,也涉及UObject变量的GC。原理参考(UE4 4.26)UE4 UObject的内存管理机制浅析

如下面所示:

UCLASS(config=Game)
class AMyProject1Character : public ACharacter
{GENERATED_BODY()private:UPROPERTY()UObject* object;}

TWeakObjectPtr

UE4里自创一套C++ GC规则,官方并不推荐使用C++标准库。 学C++的同学可能会对弱指针熟悉,可以访问一个对象又不造成引用。通常定义在一个类内,用于获取其他对象的某个变量当成临时变量方便访问。如下面所示:

UCLASS()
class MYPROJECT3_API ATestActor : public AActor
{GENERATED_BODY()public:	// Sets default values for this actor's propertiesATestActor();UPROPERTY(VisibleAnywhere)class UCameraComponent* cameraComponent;};ATestActor::ATestActor()
{// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;cameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));cameraComponent->SetupAttachment(RootComponent);}
UCLASS(config=Game)
class AMyProject3Character : public ACharacter
{GENERATED_BODY()public:TWeakObjectPtr<UCameraComponent> cameraComponent;protected:virtual void BeginPlay() override;
}void AMyProject3Character::BeginPlay()
{Super::BeginPlay();TArray<AActor*> arrayActor;UGameplayStatics::GetAllActorsOfClass(GetWorld(), ATestActor::StaticClass(), arrayActor);if (arrayActor.Num() > 0){ATestActor* testActor = Cast<ATestActor>(arrayActor[0]);if (nullptr == testActor)return;cameraComponent = testActor->cameraComponent;}
}

这里CameraComponent组件创建与Te'stActor,但是AMyproject2Character需要保存临时变量的时候就用弱指针保存。

局部创建的UObject对象GC

局部创建并且在局部使用的UObject要调用AddToRoot来防止被GC, 然后 RemoveFromRoot来移除引用能得到GC

UCLASS()
class MYPROJECT4_API UTestObject : public UObject
{GENERATED_BODY()public:UTestObject(){}float a;};void AMyProject4Character::BeginPlay()
{Super::BeginPlay();UTestObject* testObject = NewObject<UTestObject>(this);testObject->AddToRoot();testObject->a = 1.0f;UE_LOG(LogTemp, Error, TEXT("xxxx = %f"), testObject->a);testObject->RemoveFromRoot();
}

普通结构体Struct的GC

不继承UObject和UStruct对象的结构体的GC有下面几条

TSharedPtr

共享智能指针,类似C++标准库的共享智能指针,不能用于UObject对象(TWeakPtr倒是可以用于UObject), 因为UObject有自己 专门GC的一套规则。总之TSharedPtr用于自定义的结构体(不继承UObject)。

如下所示:

class MYPROJECT2_API FTest
{
public:FTest();~FTest();
};#include "FTest.h"FTest::FTest()
{UE_LOG(LogTemp, Warning, TEXT("1111FTest Constrction"));
}FTest::~FTest()
{UE_LOG(LogTemp, Warning, TEXT("2222FTest Deconstrction"));
}
UCLASS()
class MYPROJECT2_API ATestActor : public AActor
{GENERATED_BODY()public:    // Sets default values for this actor's propertiesATestActor();TSharedPtr<FTest> Test;
protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;}// Called when the game starts or when spawned
void ATestActor::BeginPlay()
{Super::BeginPlay();Test = MakeShareable(new FTest());}

PIE运行时Actor创建:

PIE取消运行Actor销毁:

如果是局部变量的结构体创建TSharedPtr:


void ATestActor::BeginPlay()
{Super::BeginPlay();TSharedPtr<FTest> Test;Test = MakeShareable(new FTest());}

FGCObject

上面我们说到了UObject的情况,采用NewObject创建和UPROPERTY()标记来进行GC。而不继承UObject的自定义结构体采用MakeShareable来创建SharedPtr来管理GC。这时候一个特殊的情况出现了玩意,一个不继承UObject的结构体中出现UObject对象怎么办?如下面所示:

 */
UCLASS(BlueprintType, notplaceable, MinimalAPI)
class UCameraAnim : public UObject
{。。。。。。。。。。。。。。。。。。。。。。。。
}class UCameraAnim;
/*** */class MYPROJECT2_API FTest
{
public:FTest();~FTest();UPROPERTY()UCameraAnim* CameraAnim;
};

这种情况下,我试过,如果用MakeShareable创建结构体指针,然后直接操作结构体的UObject对象进行NewObjec创建,结构体本身的访问没问题,但是结构体里面的UObject对象一段时间后自动GC掉,成为野指针,不能访问。这种情况怎么解决呢?UE4为我们提供了FGCObject ,我们的结构体继承FGCObject ,然后用AddReferencedObjects 方法引用结构体里的所有UObject对象,这里的UObject对象不需要被UPROPERTY()标记, 如下所示

#include "GCObject.h"class UCameraAnim;
/*** */
class MYPROJECT2_API FTest :public FGCObject
{
public:FTest();~FTest();UCameraAnim* CameraAnim;protected:virtual void AddReferencedObjects(FReferenceCollector& Collector) override{Collector.AddReferencedObject(CameraAnim);}
};

TSharedRef

本质上共享引用(TSharedRef)和共享指针(TSharedPtr)是差不多的,一样是用于非UObject体系的对象,具备引用计数器,不过TSharedRef和TSharedPtr的很大不同在于,共享引用无法引用空对象,就像C++ 声明空引用,编译直接报错。TSharedRef连
“IsValid”判断是否非空都没有,强逼你使用必定是有效的引用。所以在UE4 的SWidget编程大量使用TSharedRef<SWidget>

如 int& a;

正确的声明定义

TSharedRef<FMyCustom> MyCustom = MakeShared<FMyCustom>();

错误的声明定义:

TSharedRef<FMyCustom> MyCustom;TSharedRef<FMyCustom> MyCustom = nullptr;

TSharedRef没有空引用,应该持有的对象必须是有效的

TSharedPtr转为TSharedRef

TSharedPtr<FMyCustom> aaa = MakeShareable(new FMyCustom);
TSharedRef<FMyCustom> a = aaa.ToSharedRef();

一个普通的结构体使用TSharedFromThis继承,就可以拥有AsShared函数让普通的结构体对象转为TSharedRef, 像SWidget这个类本身就使用了TSharedFromThis

struct FMyCustom : TSharedFromThis<FMyCustom>
{int a = 1;FMyCustom(){UE_LOG(LogTemp, Error, TEXT("destruct a = %d"), a);}~FMyCustom(){UE_LOG(LogTemp, Error, TEXT("undestruct a = %d"), a);}
};FMyCustom MyCustom;
RefCustom = MyCustom.AsShared();

TSharedRef可以转化为TSharedPtr, 直接等于就可以了

	TSharedRef<FMyCustom> RefCustom = MakeShared<FMyCustom>();TSharedPtr<FMyCustom> PtrCustom = RefCustom;

TWeakPtr

上面我们说到了 TWeakObjectPtr 为专门用于UObject, 相应的普通结构体也有弱指针TWeakPtr, 能够访问一个非UObject对象,但是又不造成引用计数+1

一般来说通过TWeakPtr来访问其持有的对象,先是用pin转换为TSharePtr指针,判断对象是否有效后,再访问,而不是直接通过弱指针的IsValid,然后直接访问

struct FMyCustom
{int a = 1;FMyCustom(){UE_LOG(LogTemp, Error, TEXT("destruct a = %d"), a);}~FMyCustom(){UE_LOG(LogTemp, Error, TEXT("undestruct a = %d"), a);}
};UCLASS()
class MYPROJECT5_API AMyActor : public AActor
{
public:TWeakPtr<FMyCustom> WeakCustom;virtual void BeginPlay() override;virtual void Tick(float DeltaTime) override;}void AMyActor::BeginPlay()
{Super::BeginPlay();TSharedPtr<FMyCustom> ShareCustom = MakeShareable(new FMyCustom);WeakCustom = ShareCustom;}// Called every frame
void AMyActor::Tick(float DeltaTime)
{Super::Tick(DeltaTime);TSharedPtr<FMyCustom> ShareCustom = WeakCustom.Pin();if (ShareCustom.IsValid()){UE_LOG(LogTemp, Error, TEXT("a = %d"), ShareCustom->a);}}

很显然,弱指针没有引用到局部的智能创建的对象,所以没有“Tick a  = 1”的log

TSharedPtr,TSharedRef, TWeakPtr在容器使用事项

得注意:弱指针(TWeakPtr)对象不能作为Set或者Map的Key,因为一个对象被GC掉无法通知一个容器的Key, 像TMap<TWeakPtr<FMyCustom>, int>就是不正当的使用手法,当然TWeakPtr可以作为容器的Value,  TMap<int,TWeakPtr<FMyCustom>>毫无问题。

而TSharedPtr和TSharedRef既可以当作容器的Key,也可以作为容器的Value.

参考资料

[1] https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/SmartPointerLibrary/index.html

这篇关于(UE4 4.20 )UE4的GC(垃圾回收)编程规范的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/250369

相关文章

离心萃取机废旧磷酸铁锂电池回收工艺流程

在废旧磷酸铁锂电池的回收工艺流程中,离心萃取机主要应用于萃取除杂的步骤,以提高回收过程中有价金属(如锂)的纯度。以下是结合离心萃取机应用的废旧磷酸铁锂电池回收工艺流程: 电池拆解与预处理 拆解:将废旧磷酸铁锂电池进行拆解,分离出电池壳、正负极片、隔膜等部分。破碎与筛分:将正负极片进行破碎处理,并通过筛分将不同粒径的物料分开,以便后续处理。 浸出与溶解 浸出:采用适当的浸出工艺(如二段式逆

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

Java并发编程之——BlockingQueue(队列)

一、什么是BlockingQueue BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种: 1. 当队列满了的时候进行入队列操作2. 当队列空了的时候进行出队列操作123 因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空

HotSpot虚拟机的经典垃圾收集器

读《深入理解Java虚拟机》第三版笔记。 关系 Serial、ParNew、Parallel Scavenge、Parallel Old、Serial Old(MSC)、Concurrent Mark Sweep (CMS)、Garbage First(G1)收集器。 如图: 1、Serial 和 Serial Old 收集器 2、ParNew 收集器 3、Parallel Sc