UE5 深入之 UObject

简介: 今天更新了一下UE官方源码的 release 分支,之前都是看结构和大概,现在需要看具体细节了,就更新到最新吧,先从UObject 开始深入了解吧。注意 C++ 的语法和文件结构,一个类可能会写在不同的文件当中

开始阶段

当我打开 UE5 Engine/Source/Runtime/CoreUObject 目录的时候,惊呆我了,文件太多了,随便打开一个头文件都是一拖不见低,我要是一个一个老老实实的看,那单这一个模块我就能看很久。
还是借助 AI 为我画一下重点吧。这里附上官网的 UObject 的文档

CoreUObject 模块数量庞大,要一个一个看太不现实了,我们捡重点的看。

CoreUObject 必看的类

第一优先级 基石

  • UObject: 所有UE对象的根
  • UClass: 元数据核心

第二优先级 运行机制

  • UWorld: 场景管理
  • FProperty: 属性系统重构后的核心
  • UStruct: 内存结构基础

第三优先级 高级机制

  • UGarbageCollection:垃圾回收
  • UFunction: 函数反射

UObject项目内各类对象继承关系

class UObjectBase;
class UObjectBaseUtility;
class   UObject;
class       UField;
class           UEnum;
class           FProperty;
class               FObjectProperty;
class           UStruct;
class               UFunction;
class               UClass;
class               UScriptStruct;
class       FLinker;
class           FLinkerLoad;
class           FLinkerSave;
class       UPackage;
class       USystem;
class       UTextBuffer;
class       UPackageMap;
class       UObjectRedirector;
class FField;
class   FProperty;
class       FByteProperty;
class       FUInt16Property;
class       FUInt32Property;
class       FUInt64Property;
class       FInt8Property;
class       FInt16Property;
class       FIntProperty;
class       FInt64Property;
class       FBoolProperty;
class       FFloatProperty;
class       FDoubleProperty;
class       FObjectPropertyBase;
class       FObjectProperty;
class           FClassProperty;
class           FInterfaceProperty;
class           FWeakObjectProperty;
class           FLazyObjectProperty;
class           FSoftObjectProperty;
class               FSoftClassProperty;
class       FNameProperty;
class       FStructProperty;
class       FStrProperty;
class       FTextProperty;
class       FArrayProperty;
class       FDelegateProperty;
class       FMulticastDelegateProperty;
class       FMapProperty;
class       FSetProperty;
class       FEnumProperty;
class       FUtf8StrProperty;
class       FAnsiStrProperty;

所以我们筛选出了几个重点的类,接下来看一下 UObject 类吧

UObject

官网的 UObject 的文档
打开 UObject 的头文件,发现内容也是巨大无比,只能在请AI 帮忙画重点了
UObject 有200+ 的方法,我们检索一些重要的来看

重点关注函数

类别 关键函数 作用描述 重要性
生命周期 StaticConstructObject_Internal() 对象创建的实际入口 ★★★★★
生命周期 ConditionalBeginDestroy() 销毁流程起点 ★★★★★
内存管理 MarkPendingKill() / MarkAsGarbage() 标记待垃圾回收 ★★★★☆
内存管理 AddToRoot() / RemoveFromRoot() 控制GC生存周期 ★★★★☆
元数据 GetClass() 获取对象UClass ★★★★★
元数据 StaticClass() 获取本类UClass ★★★★☆
元数据 GetName() / GetFullName() 对象名称查询 ★★★☆☆
关系系统 GetOuter() 获取所属容器对象 ★★★★☆
关系系统 GetPackage() 获取所属资源包 ★★★☆☆
序列化 Serialize(FArchive& Ar) 对象数据持久化核心 ★★★★☆
反射调用 ProcessEvent(UFunction, void) 动态函数调用的核心入口 ★★★★☆
对象状态 IsValid() 检查对象有效性 ★★★★☆
对象状态 HasAnyFlags() 状态标志检查 ★★★☆☆
对象查找 StaticFindObject() 全局对象查找 ★★★☆☆
初始化 PostInitProperties() 属性初始化后处理 ★★★☆☆
线程安全 IsInGameThread() 检查当前线程 ★★★☆☆

函数关系图谱

flowchart TD

    A[对象创建] --> B[StaticConstructObject_Internal]
    B --> C[调用 Class->ClassConstructor]
    C --> D[调用 FObjectInitializer 初始化属性]
    D --> E[执行 PostInitProperties]

    F[对象使用] --> G[GetClass]
    G --> H[反射访问 UProperty]
    G --> I[调用 UFunction via ProcessEvent]

    J[对象销毁] --> K[ConditionalBeginDestroy]
    K --> L[执行 BeginDestroy]
    L --> M[等待垃圾回收]
    M --> N[执行 FinishDestroy]

好了,重点已经画出来,可以一点一点啃食了

阅读记录

StaticConstructObject_Internal

接下来我将像之前一样,记录一些在阅读过程中遇到的比较有意思或者我没有见过的东西

  • UObject 被定义在 Engine/Source/Runtime/CoreUObject/Public/UObject/Object.h 文件内

  • UObject 继承自 UObjectBaseUtility 继承自 UObjectBase

  • 我们需要额外关注的文件 UObjectGlobals.cpp 第一个 StaticConstructObject_Internal 函数就放在了这里,他返回一个 UObject * 指针,看名字就知道它负责创建一个 UObject

  • FStaticConstructObjectParameters 这个结构体是用来做为 StaticConstructObject_Internal 函数的参数的里面包含了很多创建时需要的数据例如新 object 的名字,类型,创建这个object 的外部物体的指针

  • EObjectFlags 创建物体时指定的一个 flag,会影响物体的创建行为,大概有30+的flag

  • EInternalObjectFlags 内部标签,引擎内部使用的

  • COREUOBJECT_API 模块导出宏,表明该结构在 CoreUObject 模块中定义并对外开放,这个宏使用 ctrl + 左键点击 没有办法跳转到定义

  • CDO Class Default Object

  • StaticAllocateObject Object 内存分配函数,返回一个 UObject*指针,这个函数在 StaticConstructObject_Internal 内被调用,也就是实际的 UObject 创建函数。老长了小 400 多行

  • FGCReconstructionGuard 在创建 UObject 的过程中,有一小节程序使用 GCGuard 来防止错误的回收
    编辑器模式下,创建完成的最后会 FCoreUObjectDelegates::OnObjectConstructed.Broadcast(Result); 广播一次

ConditionalBeginDestroy

声明在 Obj.h 定义在 Obj.cpp,UObject 被销毁时会被调用
整体流程比较简单,就是标记自己为要销毁,移除各种引用和package
SpikeMark 内存激增分析工具

MarkObjectsPendingKill 和 MarkAsGarbage 函数

MarkObjectsPendingKill 我查找的结果是定义在了 World.cpp内,在 UWorld 类里面
MarkAsGarbage 函数直接定义在 UObjectBaseUtility.h
FORCEINLINE 强制内联宏,可以将函数在编译时嵌入到调用的地方,这样就可以消除调用开销,但是会导致代码膨胀

AddToRoot 和 RemoveFromRoot 函数

AddToRoot 函数直接定义在 UObjectBaseUtility.h 文件内,将当前物体设置到 root 集,防止它和他的子节点被垃圾回收
RemoveFromRoot 函数直接定义在 UObjectBaseUtility.h 文件内 ,它的作用就是将 物体从 root 集移出

  • 这两个函数都没有参数,他们内部的方法使用 UObjectBase 的 InternalIndex 作为参数来确定是哪个物体

GetClass 和 StaticClass 函数

GetClass 函数直接定义在 UObjectBase.h
StaticClass 函数的定义比较奇特,无法直接索引到,他是通过DECLARE_CLASS 宏定义出来的,但这个宏没有什么实际的操作

  • DECLARE_CLASS 为 UHT 标记处理位置,
    UHT 实现如下

  // 你的头文件 UObject.h 中有:  
  DECLARE_CLASS(...)

  // UEHeaderTool 扫描后生成:
  // 在 GeneratedCode 文件夹生成 UObject.gen.cpp:
  UClass* UObject::StaticClass()
  {
      static UClass* Class = ...;
      return Class;
  }

#define DECLARE_CLASS( TClass, TSuperClass, ... ) \
DECLARE_CLASS_INTRINSIC_NO_CTOR(TClass, ...) \
static UClass* GetPrivateStaticClass();

其余函数比较简单,就大体标记一下定义位置

GetName 和 GetFullName 函数

GetName 函数直接定义在 UObjectBaseUtility.h 文件内
GetFullName 声明在 UObjectBaseUtility.h 内,定义在 UObjectBaseUtility.cpp

GetOuter 和 GetPackage 函数

GetOuter 定义在 UObjectBase.h
GetPackage 定义在 UObjectBaseUtility.cpp

Serialize 函数

定义在 Obj.cpp 内,使用的是IMPLEMENT_FARCHIVE_SERIALIZER

ProcessEvent 函数

定义在 ScriptCore.cpp

IsValid 函数

直接定义在 Object.h

HasAnyFlags 函数

定义在 UObjectArray.h

StaticFindObject 函数

定义在 UObjectGlobals.cpp
INC_DWORD_STAT 宏 是一个性能统计的关键操作,它会增加一个32为整数(DWORD)统计值的计数器

PostInitProperties 函数

定义在 UObjectGlobals.cpp

IsInGameThread 函数

定义在 ThreadingBase.cpp
CORE_API 宏 定义DLL 接口导出

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • UE必须的版权声明,否则CIS会生成失败! // Copyright XXX Games, Inc. All Ri...
    游戏开发程序员阅读 938评论 0 1
  • 简介: 经过前面的努力,边看,边记,边梳理,现在已经对 UE5 Solution 的结构有了一定的了解,而且也一步...
    暴走TA阅读 78评论 0 1
  • 简介: 捋完 ProjectBrowser 的启动项目流程,心情甚是舒畅,现在来开始重新梳理 Launch 流程,...
    暴走TA阅读 196评论 0 1
  • 简介: 前面的文章在 Launch.cpp 里找到了引擎启动入口,但是苦于一直找不到刚打开引擎时的那个可以选择项目...
    暴走TA阅读 169评论 0 1
  • Actor介绍和作用 Actor类是游戏中一切实体Actor的基类(继承UObject)。 代表着游戏中的一个实体...
    游戏开发程序员阅读 1,223评论 0 1

友情链接更多精彩内容