简介: 今天更新了一下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 接口导出