UE4反射和垃圾回收

01 反射

什么是反射

反射是程序在运行时检查自身,将参与反射的数据(变量,类,函数等等)暴露给蓝图,允许运行时去调用。

反射的使用

宏就是用来帮助建立反射的,Unreal Header Tool(UHT)是建立反射的工具,它会扫描头文件。

例如:UE4中只要在一个函数的前面加上UFUNCTION()宏,然后在括号里面加上BlueprintCallable就可以在编辑器里面调用。

  • 为了标记一个头文件包含反射类型,需要添加一个头文件"XXX.generate.h"必须放在头文件的尾部,该文件一般位于“Project\工程名\Intermediate\Build\Win64\ Inc\工程名\类名.generate.h”

    #include "XXX.generate.h"
    
  • 可以使用UENUM()、UCLASS()、USTRUCT()、UFUNCTION()、以及UPROPERTY()来在头文件中注解不同的类型以及成员变量。每一个宏都会出现在类型或者成员变量的前面,并且可以包含额外的修饰符关键字。

    UCLASS() //标识类参与反射
    
    UPROPERTY()//标识属性参与反射
    
    UFUNCTION()//标识函数参与反射
    
    GENERATED_CODE//反射后的结果放置位置
    
  • UHT对代码文件内容进行处理,生成 .generated.h/.gen.cpp 文件,在生成的代码中加入反射信息。头文件中包一些函数实现声明的补全,对Current FileId的赋值,将一些函数注册到蓝图的虚拟中的操作。cpp文件(全局唯一)中会生成一个静态全局变量,在静态全局变量的构造函数中进行信息的注册,并将结果保存在UClass对象中(存放真实信息)。

反射的类型层次
img

UStruct是所有 聚合结构体的基础类型(包含其它成员的类型,比如一个C++类、结构体、或者函数),不应该跟C++中的结构体(struct)混为一谈(那是UScriptStruct)。UClass可以包含函数、属性以及它们的孩子,而UFunctionUStriptStruct只能包含属性。

img
反射实现机制

Unreal Build Tool(UBT)Unreal Header Tool (UHT)两个协同工作来生成运行时反射需要的数据。UBT属性通过扫描头文件,记录任何至少有一个反射类型的头文件的模块。如果其中任意一个头文件从上一次编译起发生了变化,那么 UHT就会被调用来利用和更新反射数据。UHT分析头文件,创建一系列反射数据,并且生成包含反射数据的C++代码(放到每一个模块的moulde.generated.inl中。注:最新版会生成到moudle.generated.cpp中),还有各种帮助函数以及thunk函数(每一个 头文件.generated.h

用生成的C++代码来存储反射数据的一个最大好处就是,它可以保证跟二进制做到同步。你永远也不会加载陈旧或者过时的反射数据,因为它是跟引擎的其它代码同时编译的,并且它会在程序启动的时候使用C++表达式来计算成员偏移等,而不是通过针对特定平台/编译器/优化的组合中进行逆向工程。

生成的诸如StaticClass()、StaticStruct()函数是为了让当前类型更好的获取反射数据,以及那此转换函数(thunks)用来在蓝图或者网络复制中调用C++函数。这些必须声明为类或者结构体的一部分,这也就解释了为什么GENERATED_UCLASS_BODY() or GENERATED_USTRUCT_BODY()宏会包含在你的反射系统的类型中,而#include "TypeName.generated.h"的头文件中定义了这些宏。

反射实例
#include "MyObject.generated.h"//虚幻引擎将生成所有反射数据并将放入此文件
 
UCLASS(Blueprintable)//Blueprintable:此类可由蓝图延展
class UMyObject : public UObject
{
    GENERATED_BODY()
 
public:
    MyUObject();
 
    UPROPERTY(BlueprintReadOnly, EditAnywhere)//BlueprintReadOnly:此属性只可从蓝图读取,不可写入
    float ExampleProperty;//EditAnywhere:说明此属性可通过属性窗口在原型和实例上进行编辑
 
    UFUNCTION(BlueprintCallable)//BlueprintCallable:公开属性在蓝图代码中调用
    void ExampleFunction();
};

02 垃圾回收机制(GC)

UE4中的GC是追踪式、非实时、精确式,非渐近、增量回收(时间片),先标记后回收的过程,为了提高效率和减少回收过程中的卡顿,可以做到并行标记和增量回收以及通过簇来提高回收的效率等。详解链接

UObject和垃圾回收

UE4的垃圾收集比较自动化,通过垃圾回收便无需手动删除 UObjects,只需维持对它们的有效引用即可。类须派生自 UObject,才能启用垃圾回收。

UCLASS()
class MyGCType : public UObject
{
    GENERATED_BODY()
};

在垃圾回收器中存在称为根集的概念,此根集是一个对象列表,回收器不会对这些对象进行垃圾回收。只要根集中的对象到讨论中的对象之间存在引用路径,对象便不会被垃圾回收。如对象到根集的此路径不存在,它便会被识别为无法达到,垃圾回收器下次运行时便会将其收集(删除),引擎以特定间隔运行垃圾回收器。

怎样才算是一个引用?存储在一个UPROPERTY属性中的UObject 对象指针。让我们看看简单的例子:

void CreateDoomedObject()
{
    MyGCType* DoomedObject = NewObject<MyGCType>();

当我们调用上面的方法时,我们实例化一个新的UObject对象,但我们没有存储对象指针到任何UPROPERTY的属性中,所以它不是根集的一部分。事实上,垃圾回收器会检测这个对象是否无法访问,若是,则回收它。

UE4采用了标记-清扫垃圾回收方式,是一种经典的垃圾回收方式。一次垃圾回收分为两个阶段。第一阶段从一个根集合出发,遍历所有可达对象,遍历完成后就能标记出可达对象和不可达对象了,这个阶段会在一帧内完成。第二阶段会渐进式的清理这些不可达对象,因为不可达的对象将永远不能被访问到,所以可以分帧清理它们,避免一下子清理很多UObject,比如map卸载时,发生明显的卡顿。

Actor和垃圾回收

Actors 通常不会被垃圾回收。Actors 生成后,必须手动调用 Destroy()。它们不会被立即删除,而会在下个垃圾回收阶段被清理。

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()
 
public:
    UPROPERTY()
    MyGCType* SafeObject;
 
    MyGCType* DoomedObject;
 
    AMyActor(const FObjectInitializer& ObjectInitializer)
        :Super(ObjectInitializer)
    {
        SafeObject = NewObject<MyGCType>();
        DoomedObject = NewObject<MyGCType>();
    }
};
 
void SpawnMyActor(UWorld* World, FVector Location, FRotator Rotation)
{
    World->SpawnActor<AMyActor>(Location, Rotation);
}

调用上述函数时,将在世界场景中生成一个actorActor的构建函数创建两个对象。一个指定到 UPROPERTY,另一个指定到裸指针。Actors自动成为根集的一部分,SafeObject将不会被垃圾回收,因为它从根集对象出到达。然而 DoomedObject的进展不是十分顺利。我们未将其标为UPROPERTY,因此回收器并不知道其正在被引用,而会将它逐渐销毁。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值