3. FLinkerLoad, FLinkerSave分别是UObject的反序列化和序列化的内核
3.0. UPackage与UObject
UObject因为涉及与其他UObject的复杂引用关系,如果我们客制化地单独正反序列化每一个UObject,我们会在反序列化的时候惊觉这是繁琐而不可能的。
为了满足UObject这种复杂的对象的序列化,UE选择将UObject和一大批额外信息整合为UPackage,
以正反序列化UPackage的方式,将UObject连带着这堆信息一齐正反序列化
3.1. SVO
UObject序列化:获得与该UObject有外部链关系的UPackage后,FLinker凭借合法的UPackage创建FLinkerSave,并序列化UPackage
UObject反序列化:获得UPackage后,FLinker凭借合法的UPackage创建FLinkerLoad,并籍此反序列化UPackage
UObject反序列化相当复杂,SVO的说明只能引领整体思考
实际观察的反序列化堆栈后,我们会发现:
LoadObject()->StaticLoadObject()
LoadClass()->StaticLoadObject()
->StaticLoadObject()->StaticLoadObjectInternal ->ResolveName
-尝试使用传入的字符串来获得upackage,并处理出ObjectName-
[ResolveName成功,获得upackage]->StaitcFindObjectFast
->StaticFindObjectFastInternal
-全局UObject哈希表查找-
[ResolveName失败]->[字符串可疑(没有‘.’) ]-> -修补字符串- ->StaticFindObjectFastInternal
-全局UObject哈希表查找-
3.2. 关键继承关系
FLinkerLoad, FLinkerSave
class FLinkerLoad : public FLinker, public FArchiveUObject
class FLinkerSave : public FLinker, public FArchiveUObject
class FLinker : public FLinkerTables
FArchiveUObject
class FArchiveUObject : public FArchive
class FArchive : private FArchiveState
3.3. 类层级与基本功能说明
3.3.1. FArchiveUObject
其中四个静态函数和<<运算符重载,本质上负责了序列化FLazyObjectPtr, FSoftObjectPtr, FSoftObjectPath, FWeakObjectPtr四个特殊的UObjectPtr类型
其中的实现相当简单,在特殊条件下序列化对应object的guid,正常情况下序列化UObject*
3.3.2. FLinkerTables
还记得前文提到的UPackage的组成结构吗?Summary, Names, Imports Table, Exports Table, Depends Table, ImportGuids Map, ExportGuids Map, Thumbnails, Export-Objects
FLinkerTables结构体里直接定义了 Names, Imports Table, Exports Table, Depends Table,显然它是反序列化时存储uobject的其他相关数据的重要结构体
3.3.3. FLinker
UE注释里是这样介绍FLinker的,管理与ue相关的package数据,充当磁盘内容和内存中的UPackage的桥梁
Manages the data associated with an Unreal package. Acts as the bridge betweenthe file on disk and the UPackage object in memory for all Unreal package types.
从这一段话可能难以注意到的信息,事实上FLinker在作用前总是需要磁盘内容和内存中的UPackage同时有效,正如桥梁不能悬空作用,它需要桥墩的两端都有效
FLinker里定义了 Summary
3.3.4. FLinkerLoad
FLinkerLoad的Tick函数将各种UPackage的除ExportObject数据从uasset中加载进来
最后调用LoadAllObjects彻底完成反序列化
3.3.5. FLinkerSave
原谅我能力有限,以下内容不一定准确
FLinkerSave在package.h的静态函数Save里的应用,比如接受该类基类archive的serialize函数是重写在各个客制化的UObject派生类里的,
更贴近的应用,反而更多是使用linker的tell和seek作为工具类,或者使用其中的结构体来存储逻辑标识而不是实际写入的数据来作为辅助类
真正写入二进制数据的函数是这个SavePackageUtilities::WriteToFile
3.3.6. Thumbnails 在FLinkerManager::EnsureLoadingComplete被加载
3.4. 调用堆栈
3.4.1 UObject序列化
UPackage::SavePackage()->
UPackage::Save()->
->2711-2862 序列化CDO或者依靠子类的重写serialize函数序列化子类
//在此处查找这个serialize的引用,很多子类,某某actor都重写了它
-2899-3023 标记相关object,class,package的名称
-// Build ImportMap.
-// Build ExportMap.
-...
// Save the import map.
// Save the export map.
-...
-finished
3.4.2 UObject反序列化
LoadObject()->
StaticLoadObject()->
StaticLoadObjectInternal()->
ResolveName()//由StrName(其实是传入的路径TChar*参数)查找UPackage,找不到会创建一个
LoadPackage()
StaticFindObjectFast()
StaticFindObjectFastInternal()
StaticFindObjectFastInternalThreadSafe()
//使用全局UObject的哈希表查询
//...杂七杂八的路径修补后重新尝试
LoadPackage()是实际的反序列化步骤
LoadPackage()->
LoadPackageInternal()->
//...//寻找package
GetPackageLinker()->
//寻找或者创建一个FLinkerLoad* Linker
FLinkerLoad::CreateLinker()->
Tick()
//一系列反序列化在此完成
Linker->LoadAllObjects()
// 加载UObejcts