UE5 反射源码分析
UE5反射
分析基于ue5.44版本
.gen.cpp分析均基于这个文件:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReflectTestActor.generated.h"
USTRUCT()
struct FMyStruct
{
GENERATED_BODY()
int a;
float b;
};
UCLASS(Blueprintable)
class ACTIONRPG_API AReflectTestActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AReflectTestActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY()
float Speed;
UPROPERTY(Replicated)
float Health;
UFUNCTION()
void AddSpeed(float InSpeed);
UFUNCTION(Server, Reliable)
void RPC_Test(float InDamage);
void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
};
//-------------------------------------------展开后👇-----------------------------------------------
UCLASS()
class ACTIONRPG_API AReflectTestActor : public AActor
{
//GENERATED_BODY()
#define FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_12_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS
public:
//FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_12_RPC_WRAPPERS_NO_PURE_DECLS
virtual void RPC_Test_Implementation(float InDamage);
static void execRPC_Test( UObject* Context, FFrame& Stack, RESULT_DECL );
static void execAddSpeed( UObject* Context, FFrame& Stack, RESULT_DECL );
//FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_12_CALLBACK_WRAPPERS \
//空宏
//FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_12_INCLASS_NO_PURE_DECLS \
private:
static void StaticRegisterNativesAReflectTestActor();
friend struct Z_Construct_UClass_AReflectTestActor_Statics;
public:
//DECLARE_CLASS(AReflectTestActor, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/ActionRPG"), NO_API) \
//#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI )
private:
AReflectTestActor& operator=(AReflectTestActor&&);
AReflectTestActor& operator=(const AReflectTestActor&);
TRequiredAPI static UClass* GetPrivateStaticClass();
public:
/** Bitwise union of #EClassFlags pertaining to this class.*/
static constexpr EClassFlags StaticClassFlags=EClassFlags(COMPILED_IN_FLAGS(0 | CLASS_Config));
/** Typedef for the base class ({{ typedef-type }}) */
typedef AActor Super;
/** Typedef for {{ typedef-type }}. */
typedef AReflectTestActor ThisClass;
/** Returns a UClass object representing this class at runtime */
inline static UClass* StaticClass()
{
return GetPrivateStaticClass();
}
/** Returns the package this class belongs in */
inline static const TCHAR* StaticPackage()
{
return TEXT("/Script/ActionRPG");
}
/** Returns the static cast flags for this class */
inline static EClassCastFlags StaticClassCastFlags()
{
return CASTCLASS_None;
}
/** For internal use only; use StaticConstructObject() to create new objects. */
inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \
{
return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
}
/** For internal use only; use StaticConstructObject() to create new objects. */
inline void* operator new( const size_t InSize, EInternal* InMem )
{
return (void*)InMem;
}
/* Eliminate V1062 warning from PVS-Studio while keeping MSVC and Clang happy. */
inline void operator delete(void* InMem)
{
::operator delete(InMem);
}
//DECLARE_SERIALIZER(AReflectTestActor)
friend FArchive &operator<<( FArchive& Ar, AReflectTestActor*& Res )
{
return Ar << (UObject*&)Res;
}
friend void operator<<(FStructuredArchive::FSlot InSlot, AReflectTestActor*& Res)
{
InSlot << (UObject*&)Res;
}
enum class ENetFields_Private : uint16
{
NETFIELD_REP_START=(uint16)((int32)Super::ENetFields_Private::NETFIELD_REP_END + (int32)1),
Health=NETFIELD_REP_START,
NETFIELD_REP_END=Health };
NO_API virtual void ValidateGeneratedRepEnums(const TArray<struct FRepRecord>& ClassReps) const override;
//FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_12_ENHANCED_CONSTRUCTORS \/
private:
/** Private move- and copy-constructors, should never be used */ \
AReflectTestActor(AReflectTestActor&&);
AReflectTestActor(const AReflectTestActor&);
public:
//DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AReflectTestActor);
/** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */
API AReflectTestActor(FVTableHelper& Helper);
//DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AReflectTestActor);
static UObject* __VTableCtorCaller(FVTableHelper& Helper)
{
return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) AReflectTestActor(Helper); \
}
//DEFINE_DEFAULT_CONSTRUCTOR_CALL(AReflectTestActor)
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())AReflectTestActor; }
NO_API virtual ~AReflectTestActor();
private:
PRAGMA_ENABLE_DEPRECATION_WARNINGS
public:
// Sets default values for this actor's properties
AReflectTestActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY()
float Speed;
//下面改过一次
//原来是:
/*
UFUNCTION()
void AddSpeed();
*/
UPROPERTY(Replicated)//后面增加的,给RPC用的
float Health;
UFUNCTION()
void AddSpeed(float InSpeed);//分析到ConstructClass中构造函数的时候,发现没参数,后面增加了
UFUNCTION(Server, Reliable)
void RPC_Test(float InDamage);
void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
};
//CPP:
#include "ReflectTestActor.h"
#include"net/UnrealNetwork.h"
// Sets default values
AReflectTestActor::AReflectTestActor()
{
// 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;
Speed = 1.0f;
}
// Called when the game starts or when spawned
void AReflectTestActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AReflectTestActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AReflectTestActor::AddSpeed(float InSpeed)
{
Speed += InSpeed;
}
void AReflectTestActor::RPC_Test_Implementation(float InDamage)
{
Health = InDamage;
}
void AReflectTestActor::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AReflectTestActor, Health);
}
.generated.h中生成的宏展开和分析:
下面的内容ActionRPGCharacter生成的内容
-
GENERATED_BODY()被定义在ObjectMacros.h: Helper macros and defines for UObject system中
而.generated.h和.gen.cpp则是UHT扫描我们标记的宏生成的另一些宏
-
GENERATED_BODY():一个宏拼接当前文件id、_、行号、_GENERATED_BODY生成一个新的宏,这个宏的定义在.gen.h中,后面内容也均在其内
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY); //变成下面的 FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_GENERATED_BODY#define FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_GENERATED_BODY \ PRAGMA_DISABLE_DEPRECATION_WARNINGS \ public: \ FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_RPC_WRAPPERS_NO_PURE_DECLS \ FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_CALLBACK_WRAPPERS \ FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_INCLASS_NO_PURE_DECLS \ FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_ENHANCED_CONSTRUCTORS \ private: \ PRAGMA_ENABLE_DEPRECATION_WARNINGS-
RPC_WRAPPERS_NO_PURE_DECLS:
#define FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_RPC_WRAPPERS_NO_PURE_DECLS \ virtual void HandleFire_Implementation(); \ DECLARE_FUNCTION(execHandleFire); \ DECLARE_FUNCTION(execStopFire); \ DECLARE_FUNCTION(execStartFire); \ DECLARE_FUNCTION(execOnRep_CurrentHealth); \ DECLARE_FUNCTION(execTakeDamage); \ DECLARE_FUNCTION(execSetCurrentHealth); \ DECLARE_FUNCTION(execGetCurrentHealth); \ DECLARE_FUNCTION(execGetMaxHealth);-
DECLARE_FUNCTION:声明exec函数,其实现在.gen.cpp中
#define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )
-
-
CALLBACK_WRAPPERS:空的宏
-
INCLASS_NO_PURE_DECLS:定义UClass 的主体:
#define FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_INCLASS_NO_PURE_DECLS \ private: \ static void StaticRegisterNativesAActionRPGCharacter(); \ friend struct Z_Construct_UClass_AActionRPGCharacter_Statics; \ public: \ DECLARE_CLASS(AActionRPGCharacter, ACharacter, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/ActionRPG"), NO_API) \ DECLARE_SERIALIZER(AActionRPGCharacter) \ enum class ENetFields_Private : uint16 \ { \ NETFIELD_REP_START=(uint16)((int32)Super::ENetFields_Private::NETFIELD_REP_END + (int32)1), \ CurrentHealth=NETFIELD_REP_START, \ NETFIELD_REP_END=CurrentHealth }; \ NO_API virtual void ValidateGeneratedRepEnums(const TArray<struct FRepRecord>& ClassReps) const override;-
DECLARE_CLASS:这里生成的内容很重要:
- 定义了我们常用的Super
- 定义我们常用的StaticClass()和声明了其内部的GetPrivateStaticClass(),实现在.gen.cpp中
- 用于优化Cast的CastFlag
- 重载了的自定义new和delete运算符
DECLARE_CLASS(AReflectTestActor, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/ActionRPG"), NO_API) #define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \ private: TClass& operator=(TClass&&); TClass& operator=(const TClass&); TRequiredAPI static UClass* GetPrivateStaticClass(); public: /** Bitwise union of #EClassFlags pertaining to this class.*/ static constexpr EClassFlags StaticClassFlags=EClassFlags(TStaticFlags); /** Typedef for the base class ({{ typedef-type }}) */ typedef TSuperClass Super; /** Typedef for {{ typedef-type }}. */ typedef TClass ThisClass; /** Returns a UClass object representing this class at runtime */ inline static UClass* StaticClass() { return GetPrivateStaticClass(); } /** Returns the package this class belongs in */ inline static const TCHAR* StaticPackage() { return TPackage; } /** Returns the static cast flags for this class */ inline static EClassCastFlags StaticClassCastFlags() { return TStaticCastFlags; } /** For internal use only; use StaticConstructObject() to create new objects. */ \ inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \ { return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); } /** For internal use only; use StaticConstructObject() to create new objects. */ \ inline void* operator new( const size_t InSize, EInternal* InMem ) { return (void*)InMem; } /* Eliminate V1062 warning from PVS-Studio while keeping MSVC and Clang happy. */ \ inline void operator delete(void* InMem) { ::operator delete(InMem); } -
DECLARE_SERIALIZER:重载序列化使用的<<
#define DECLARE_SERIALIZER( TClass ) \ friend FArchive &operator<<( FArchive& Ar, TClass*& Res ) \ { \ return Ar << (UObject*&)Res; \ } \ friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res) \ { \ InSlot << (UObject*&)Res; \ }
-
-
ENHANCED_CONSTRUCTORS:构造函数相关,定义了拷贝构造函数和移动构造函数为Private(相当于禁止使用)
#define FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ActionRPGCharacter_h_21_ENHANCED_CONSTRUCTORS \ private: /** Private move- and copy-constructors, should never be used */ \ AActionRPGCharacter(AActionRPGCharacter&&); \ AActionRPGCharacter(const AActionRPGCharacter&); \ public: \ DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AActionRPGCharacter); \ DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AActionRPGCharacter); \ DEFINE_DEFAULT_CONSTRUCTOR_CALL(AActionRPGCharacter) \ NO_API virtual ~AActionRPGCharacter();-
DECLARE_VTABLE_PTR_HELPER_CTOR和DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER是热重载相关
-
DEFINE_DEFAULT_CONSTRUCTOR_CALL是重点,这里定义构造函数:这里使用的是placementnew,语法是new(指针)Class,在一块分配好内存的地方构造Class
#define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }
-
-
.gen.cpp分析
第一部分,.generated.h中声明的一些重要函数的实现
-
GetPrivateStaticClass实现:
TRequiredAPI static UClass* GetPrivateStaticClass(); 在.h中声明,我们平时获取CDO的StaticClass()实际上就是调用这个函数
inline static UClass* StaticClass() { return GetPrivateStaticClass(); }IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AReflectTestActor)
#define IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(TClass) \ FClassRegistrationInfo Z_Registration_Info_UClass_##TClass; UClass* TClass::GetPrivateStaticClass() \ { \ if (!Z_Registration_Info_UClass_##TClass.InnerSingleton) \ { \ /* this could be handled with templates, but we want it external to avoid code bloat */ \ GetPrivateStaticClassBody( \ StaticPackage(), \ (TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \ Z_Registration_Info_UClass_##TClass.InnerSingleton, \ StaticRegisterNatives##TClass, \ sizeof(TClass), \ alignof(TClass), \ TClass::StaticClassFlags, \ TClass::StaticClassCastFlags(), \ TClass::StaticConfigName(), \ (UClass::ClassConstructorType)InternalConstructor<TClass>, \ (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \ UOBJECT_CPPCLASS_STATICFUNCTIONS_FORCLASS(TClass), \ &TClass::Super::StaticClass, \ &TClass::WithinClass::StaticClass \ ); \ } \ return Z_Registration_Info_UClass_##TClass.InnerSingleton; \ }void GetPrivateStaticClassBody( const TCHAR* PackageName, //实参:StaticPackage(), //说明:在.generrated.h中DECLARE_CLASS 定义的StaticPackage() const TCHAR* Name, //实参:(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0) //说明:+1去掉前缀比如AActor的A,给准备废弃的类加上 UClass*& ReturnClass, //这个类的注册信息单例Z_Registration_Info_UClass_##TClass.InnerSingleton void(*RegisterNativeFunc)(),//StaticRegisterNatives##TClass,注册函数用 uint32 InSize,//sizeof(TClass),类的大小 uint32 InAlignment,// alignof(TClass),结构体对齐的字节数16/8/4/2/1 EClassFlags InClassFlags,//StaticClassFlags,解释这个类的信息的enum EClassCastFlags InClassCastFlags,//StaticClassCastFlags(), 用于在cast的时候计算类之间关系的位向量 const TCHAR* InConfigName,//配置文件名 UClass::ClassConstructorType InClassConstructor, //__DefaultConstructor UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,//hotreload的时候使用来构造虚函数表,暂时不管 FUObjectCppClassStaticFunctions&& InCppClassStaticFunctions,// GC使用的添加额外引用对象的静态函数指针,若没有定义,则会调用到UObject::AddReferencedObjects,默认函数体为空。 UClass::StaticClassFunctionType InSuperClassFn,//获取基类UClass*的函数指针 UClass::StaticClassFunctionType InWithinClassFn//获取对象外部类UClass*的函数指针,默认是UObject ) { #if WITH_RELOAD //忽略热加载 #endif //使用全局内存分配器获取一块内存,让这个单例指向其地址 ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true); ReturnClass = ::new (ReturnClass)//使用placement new 来在刚刚分配的内存上调用UClass的构造函数,构造了UClass对象(这里应该还未注册反射信息) UClass ( EC_StaticConstructor, Name, InSize, InAlignment, InClassFlags, InClassCastFlags, InConfigName, EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet), InClassConstructor, InClassVTableHelperCtorCaller, MoveTemp(InCppClassStaticFunctions) ); check(ReturnClass); //初始化Super、Outer的Uclass,里面进行与this的关联、注册Package和自身到一个队列和哈希表中 InitializePrivateStaticClass( InSuperClassFn(), ReturnClass, InWithinClassFn(), PackageName, Name ); // Register the class's native functions. RegisterNativeFunc();//StaticRegisterNatives##TClass,注册函数到Class里的TArray<FNativeFunctionLookup> NativeFunctionLookupTable; }withinClass被设置的地方,下面是UFunction的例子,为何设置withinClass后能限制其只能被声明在该Class的解释看下面:
![image-20250811152542205]()
![image-20250811152736521]()
UClass::UClass ( EStaticConstructor, FName InName, uint32 InSize, uint32 InAlignment, EClassFlags InClassFlags, EClassCastFlags InClassCastFlags, const TCHAR* InConfigName, EObjectFlags InFlags, ClassConstructorType InClassConstructor, ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller, FUObjectCppClassStaticFunctions&& InCppClassStaticFunctions ) : UStruct ( EC_StaticConstructor, InSize, InAlignment, InFlags ) , ClassConstructor ( InClassConstructor ) , ClassVTableHelperCtorCaller(InClassVTableHelperCtorCaller) , CppClassStaticFunctions(MoveTemp(InCppClassStaticFunctions)) , ClassUnique ( 0 ) , bCooked ( false ) , bLayoutChanging ( false ) , ClassFlags ( InClassFlags | CLASS_Native ) , ClassCastFlags ( InClassCastFlags ) , ClassWithin ( nullptr ) #if WITH_EDITORONLY_DATA , ClassGeneratedBy ( nullptr ) , PropertiesPendingDestruction( nullptr ) #endif , ClassConfigName () , NetFields () , ClassDefaultObject ( nullptr ) , SparseClassData ( nullptr ) , SparseClassDataStruct ( nullptr ) { // If you add properties here, please update the other constructors and PurgeClass() SetCppTypeInfoStatic(&DefaultCppClassTypeInfoStatic); // We store the pointer to the ConfigName in an FName temporarily, this cast is intentional // as we expect the mis-typed data to get picked up in UClass::DeferredRegister. PVS-Studio // complains about this operation, but AFAIK it is safe (and we've been doing it a long time) // so the warning has been disabled for now: *(const TCHAR**)&ClassConfigName = InConfigName; //-V580 }InClassConstructor调用的就是在.generated.h中生成的ENHANCED_CONSTRUCTORS中的__DefaultConstructor
/** * Helper template to call the default constructor for a class */ template<class T> void InternalConstructor( const FObjectInitializer& X ) { T::__DefaultConstructor(X); }COREUOBJECT_API void InitializePrivateStaticClass( class UClass* TClass_Super_StaticClass,//函数调用之前调用了Super的StaicClass,构造Super先再传参进来 class UClass* TClass_PrivateStaticClass,//这里就是前文构造好的StaticClass class UClass* TClass_WithinClass_StaticClass,//也是构造好了Outer的StaticClass先,再传参数 const TCHAR* PackageName, const TCHAR* Name ) { TRACE_LOADTIME_CLASS_INFO(TClass_PrivateStaticClass, Name); /* No recursive ::StaticClass calls allowed. Setup extras. */ if (TClass_Super_StaticClass != TClass_PrivateStaticClass) { TClass_PrivateStaticClass->SetSuperStruct(TClass_Super_StaticClass);//设定super } else { TClass_PrivateStaticClass->SetSuperStruct(NULL);//如果super和自己一样,就设定为NULL } TClass_PrivateStaticClass->ClassWithin = TClass_WithinClass_StaticClass;//设定OuterClass // Register the class's dependencies, then itself. TClass_PrivateStaticClass->RegisterDependencies();//这里留了一个位置给先注册依赖项,但是什么都每干,也没找到哪个UE core 类用到的 { // Defer TClass_PrivateStaticClass->Register(PackageName, Name); } }/** Enqueue the registration for this object. */ void UObjectBase::Register(const TCHAR* PackageName,const TCHAR* InName) { LLM_SCOPE(ELLMTag::UObject); //获得单例哈希表,UClass 地址做Key,PedningRegistrantInfo做Value(这个结构存储的是PackageName,和UclassNmae) TMap<UObjectBase*, FPendingRegistrantInfo>& PendingRegistrants = FPendingRegistrantInfo::GetMap(); FPendingRegistrant* PendingRegistration = new FPendingRegistrant(this); PendingRegistrants.Add(this, FPendingRegistrantInfo(InName, PackageName)); #if USE_PER_MODULE_UOBJECT_BOOTSTRAP if (FName(PackageName) != FName("/Script/CoreUObject")) { TMap<FName, TArray<FPendingRegistrant*>>& PerModuleMap = GetPerModuleBootstrapMap(); PerModuleMap.FindOrAdd(FName(PackageName)).Add(PendingRegistration); } else #endif { if (GLastPendingRegistrant) { GLastPendingRegistrant->NextAutoRegister = PendingRegistration;如果链表不为空,加入后面 } else { check(!GFirstPendingRegistrant); GFirstPendingRegistrant = PendingRegistration;//如果没链表为空,那我们现在这个就是链表头 } GLastPendingRegistrant = PendingRegistration;//我们现在是链表尾部 } }RegisterNativeFunc()调用下面的函数:
void AReflectTestActor::StaticRegisterNativesAReflectTestActor() { UClass* Class = AReflectTestActor::StaticClass(); static const FNameNativePtrPair Funcs[] = { { "AddSpeed", &AReflectTestActor::execAddSpeed }, { "RPC_Test", &AReflectTestActor::execRPC_Test }, }; FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs)); }void FNativeFunctionRegistrar::RegisterFunctions(class UClass* Class, const FNameNativePtrPair* InArray, int32 NumFunctions) { //一个个注册对于名称和地址到这个Class的TArray<FNativeFunctionLookup> NativeFunctionLookupTable; for (; NumFunctions; ++InArray, --NumFunctions) { Class->AddNativeFunction(UTF8_TO_TCHAR(InArray->NameUTF8), InArray->Pointer); } } void UClass::AddNativeFunction(const WIDECHAR* InName, FNativeFuncPtr InPointer) { FName InFName(InName); #if WITH_RELOAD //掠过热重载 #endif NativeFunctionLookupTable.Emplace(InFName, InPointer); }void UClass::AddNativeFunction(const WIDECHAR* InName, FNativeFuncPtr InPointer) { FName InFName(InName); #if WITH_RELOAD //热加载忽略 #endif NativeFunctionLookupTable.Emplace(InFName, InPointer); } Class.h: /** This class's native functions. */ TArray<FNativeFunctionLookup> NativeFunctionLookupTable;
第二部分,通过静态结构体构造函数收集反射信息(main之前)
注册以文件为单位,一个.h文件生成的ReflectTestActor_h_Statics中包含了里面用UClass、UStruct等标记的类、结构体、枚举、接口(interface)的RegisterCompiledInInfo,RegisterCompiledInInfo里包含了
// Begin Registration
struct Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics
{
static constexpr FStructRegisterCompiledInInfo ScriptStructInfo[] = {
{ FMyStruct::StaticStruct, Z_Construct_UScriptStruct_FMyStruct_Statics::NewStructOps, TEXT("MyStruct"), &Z_Registration_Info_UScriptStruct_MyStruct, CONSTRUCT_RELOAD_VERSION_INFO(FStructReloadVersionInfo, sizeof(FMyStruct), 2195146121U) },
};
static constexpr FClassRegisterCompiledInInfo ClassInfo[] = {
{ Z_Construct_UClass_AReflectTestActor, AReflectTestActor::StaticClass, TEXT("AReflectTestActor"), &Z_Registration_Info_UClass_AReflectTestActor, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AReflectTestActor), 3803201096U) },
};
};
static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_2038008627(TEXT("/Script/ActionRPG"),
Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics::ClassInfo),
Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics::ScriptStructInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics::ScriptStructInfo),
nullptr, 0);
// End Registration
PRAGMA_ENABLE_DEPRECATION_WARNINGS
这个就是上面结构体的定义,构造函数是一个可变参数模板,下面的代码块是被调用的重载版本
struct FRegisterCompiledInInfo
{
template <typename ... Args>
FRegisterCompiledInInfo(Args&& ... args)
{
RegisterCompiledInInfo(std::forward<Args>(args)...);
}
};
这个函数会遍历文件中的各个F##对应结构##RegisterCompiledInInfo,放入对应的F##对应结构##RegistrationInfo中的TArray<FRegistrant> Registrations中
void RegisterCompiledInInfo(const TCHAR* PackageName, const FClassRegisterCompiledInInfo* ClassInfo, size_t NumClassInfo, const FStructRegisterCompiledInInfo* StructInfo, size_t NumStructInfo, const FEnumRegisterCompiledInInfo* EnumInfo, size_t NumEnumInfo)
{
LLM_SCOPE(ELLMTag::UObject);
for (size_t Index = 0; Index < NumClassInfo; ++Index)
{
const FClassRegisterCompiledInInfo& Info = ClassInfo[Index];
RegisterCompiledInInfo(Info.OuterRegister, Info.InnerRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
}
for (size_t Index = 0; Index < NumStructInfo; ++Index)
{
const FStructRegisterCompiledInInfo& Info = StructInfo[Index];
RegisterCompiledInInfo(Info.OuterRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
if (Info.CreateCppStructOps != nullptr)
{
UScriptStruct::DeferCppStructOps(FTopLevelAssetPath(FName(PackageName), FName(Info.Name)), (UScriptStruct::ICppStructOps*)Info.CreateCppStructOps());
}
}
for (size_t Index = 0; Index < NumEnumInfo; ++Index)
{
const FEnumRegisterCompiledInInfo& Info = EnumInfo[Index];
RegisterCompiledInInfo(Info.OuterRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
}
}
RegisterCompiledInInfo的Class重载版本
void RegisterCompiledInInfo(class UClass* (*InOuterRegister)(), class UClass* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, FClassRegistrationInfo& InInfo, const FClassReloadVersionInfo& InVersionInfo)
{
check(InOuterRegister);
check(InInnerRegister);
FClassDeferredRegistry::AddResult result = FClassDeferredRegistry::Get().AddRegistration(InOuterRegister, InInnerRegister, InPackageName, InName, InInfo, InVersionInfo);
#if WITH_RELOAD
//略去
#endif
FString NoPrefix(UObjectBase::RemoveClassPrefix(InName));
//下面这两个函数好像都会return,等于什么都没干,ref:https://zhuanlan.zhihu.com/p/26725639107
NotifyRegistrationEvent(InPackageName, *NoPrefix, ENotifyRegistrationType::NRT_Class, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
NotifyRegistrationEvent(InPackageName, *(FString(DEFAULT_OBJECT_PREFIX) + NoPrefix), ENotifyRegistrationType::NRT_ClassCDO, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
}
RegisterCompiledInInfo的Struct重载版本
void RegisterCompiledInInfo(class UScriptStruct* (*InOuterRegister)(), const TCHAR* InPackageName, const TCHAR* InName, FStructRegistrationInfo& InInfo, const FStructReloadVersionInfo& InVersionInfo)
{
check(InOuterRegister);
FStructDeferredRegistry::Get().AddRegistration(InOuterRegister, nullptr, InPackageName, InName, InInfo, InVersionInfo);
NotifyRegistrationEvent(InPackageName, InName, ENotifyRegistrationType::NRT_Struct, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
}
AddRegistration:讲生成的反射信息加入全局表中
AddResult AddRegistration(TType* (*InOuterRegister)(), TType* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, TInfo& InInfo, const TVersion& InVersion)
{
#if WITH_RELOAD
//略去
#else
Registrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo });
return AddResult::New;
}
FRegistrant的定义:
/**
* Maintains information about a pending registration
*/
struct FRegistrant
{
TType* (*OuterRegisterFn)();
TType* (*InnerRegisterFn)();
const TCHAR* PackageName;
TInfo* Info;
#if WITH_RELOAD
TType* OldSingleton;
#endif
#if WITH_RELOAD
bool bHasChanged;
#endif
};
FClassRegisterCompiledInInfo的定义:前面四个会被传入构造FRegistrant
struct FClassRegisterCompiledInInfo
{
class UClass* (*OuterRegister)();
class UClass* (*InnerRegister)();
const TCHAR* Name;
FClassRegistrationInfo* Info;
FClassReloadVersionInfo VersionInfo;
};
//TDeferredRegistry是一个单例,里面存储着TArray<FRegistrant> Registrations
using FClassDeferredRegistry = TDeferredRegistry<FClassRegistrationInfo>;
using FEnumDeferredRegistry = TDeferredRegistry<FEnumRegistrationInfo>;
using FStructDeferredRegistry = TDeferredRegistry<FStructRegistrationInfo>;
using FPackageDeferredRegistry = TDeferredRegistry<FPackageRegistrationInfo>;
第三部分,main后的延迟注册 ProcessNewlyLoadedUObjects
ProcessNewlyLoadedUObjects是进行延迟注册的地方,在下面这两个地方被调用进行注册
EngineLoop::PreInit中的PreinitPostStartUpScreen或者
LoadModuleWithFailureReason中会广播委托,其在下面的FEngineLoop::LoadCoreModules()中
bool FEngineLoop::LoadCoreModules()
{
// Always attempt to load CoreUObject. It requires additional pre-init which is called from its module's StartupModule method.
#if WITH_COREUOBJECT
#if USE_PER_MODULE_UOBJECT_BOOTSTRAP // otherwise do it later
FModuleManager::Get().OnProcessLoadedObjectsCallback().AddStatic(ProcessNewlyLoadedUObjects);
#endif
return FModuleManager::Get().LoadModule(TEXT("CoreUObject")) != nullptr;
#else
return true;
#endif
}

ss
void ProcessNewlyLoadedUObjects(FName Package, bool bCanProcessNewlyLoadedObjects)
{
SCOPED_BOOT_TIMING("ProcessNewlyLoadedUObjects");
#if USE_PER_MODULE_UOBJECT_BOOTSTRAP
if (Package != NAME_None)
{
UObjectReleaseModuleRegistrants(Package);
}
#endif
if (!bCanProcessNewlyLoadedObjects)
{
return;
}
LLM_SCOPE(ELLMTag::UObject);
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("ProcessNewlyLoadedUObjects"), STAT_ProcessNewlyLoadedUObjects, STATGROUP_ObjectVerbose);
FPackageDeferredRegistry& PackageRegistry = FPackageDeferredRegistry::Get();//获取静态注册信息单例
FClassDeferredRegistry& ClassRegistry = FClassDeferredRegistry::Get();
FStructDeferredRegistry& StructRegistry = FStructDeferredRegistry::Get();
FEnumDeferredRegistry& EnumRegistry = FEnumDeferredRegistry::Get();
PackageRegistry.ProcessChangedObjects(true);//这些部分为热重载的部分,暂时忽略
StructRegistry.ProcessChangedObjects();
EnumRegistry.ProcessChangedObjects();
UClassRegisterAllCompiledInClasses();//第一个分析的地方,这里进行了UClass初步的构造
bool bNewUObjects = false;
TArray<UClass*> AllNewClasses;
while (GFirstPendingRegistrant ||
ClassRegistry.HasPendingRegistrations() ||
StructRegistry.HasPendingRegistrations() ||
EnumRegistry.HasPendingRegistrations())
{
bNewUObjects = true;
UObjectProcessRegistrants();//第二个分析的地方,这里进行了UClass的注册(注册到全局map和list里)
UObjectLoadAllCompiledInStructs();//第三个分析的地方,这里进行了Enum和Ustruct的反射类的构造
FCoreUObjectDelegates::CompiledInUObjectsRegisteredDelegate.Broadcast(Package);
UObjectLoadAllCompiledInDefaultProperties(AllNewClasses);//最后一个分析的地方,这里完成了UClass最后的构造(属性的构造连接、函数的构造(其里面也有属性构造连接)和连接)
}
#if WITH_RELOAD
IReload* Reload = GetActiveReloadInterface();
if (Reload != nullptr)
{
UClassReplaceReloadClasses(); // Legacy
PackageRegistry.NotifyReload(*Reload);
EnumRegistry.NotifyReload(*Reload);
StructRegistry.NotifyReload(*Reload);
ClassRegistry.NotifyReload(*Reload);
Reload->Reinstance();
}
#endif
PackageRegistry.EmptyRegistrations();
EnumRegistry.EmptyRegistrations();
StructRegistry.EmptyRegistrations();
ClassRegistry.EmptyRegistrations();
if (TMap<UObjectBase*, FPendingRegistrantInfo>& PendingRegistrants = FPendingRegistrantInfo::GetMap(); PendingRegistrants.IsEmpty())
{
PendingRegistrants.Empty();
}
if (bNewUObjects && !GIsInitialLoad)
{
for (UClass* Class : AllNewClasses)
{
// Assemble reference token stream for garbage collection/ RTGC.
if (!Class->HasAnyFlags(RF_ClassDefaultObject) && !Class->HasAnyClassFlags(CLASS_TokenStreamAssembled))
{
Class->AssembleReferenceTokenStream();
}
}
}
}
UClassRegisterAllCompiledInClasses:
这里干的事情就是每个文件里的UClass(),都会在这里调用StaticClass使用反射生成的内容初步构造UClass(是innersingleton):为UClass对象申请内存,通过大钊文章里的细节讲解,这里进行的初步构造应该是防止后面Ustruct嵌套UClass导致的循环调用的问题,UClass不完全的构造也是因为UStruct还没构造,需要等待UStruct和Enum的构造完成。
void UClassRegisterAllCompiledInClasses()
{
#if WITH_RELOAD
TArray<UClass*> AddedClasses;
#endif
SCOPED_BOOT_TIMING("UClassRegisterAllCompiledInClasses");
LLM_SCOPE(ELLMTag::UObject);
FClassDeferredRegistry& Registry = FClassDeferredRegistry::Get();
Registry.ProcessChangedObjects();
for (const FClassDeferredRegistry::FRegistrant& Registrant : Registry.GetRegistrations())
{
UClass* RegisteredClass = FClassDeferredRegistry::InnerRegister(Registrant);//这里调用innerRegister最终调用StaticClass
#if WITH_RELOAD
if (IsReloadActive() && Registrant.OldSingleton == nullptr)
{
AddedClasses.Add(RegisteredClass);
}
#endif
}
#if WITH_RELOAD
if (AddedClasses.Num() > 0)
{
FCoreUObjectDelegates::ReloadAddedClassesDelegate.Broadcast(AddedClasses);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FCoreUObjectDelegates::RegisterHotReloadAddedClassesDelegate.Broadcast(AddedClasses);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
#endif
}
static TType* InnerRegister(const FRegistrant& Registrant)
{
return Registrant.InnerRegisterFn();
}
FRegistrant的定义:
/** * Maintains information about a pending registration */ struct FRegistrant { //这里以AReflectTestActor的FClassRegisterCompiledInInfo为例填入信息 TType* (*OuterRegisterFn)();// Z_Construct_UClass_AReflectTestActor TType* (*InnerRegisterFn)();//AReflectTestActor::StaticClass const TCHAR* PackageName;// TEXT("AReflectTestActor") TInfo* Info;//&Z_Registration_Info_UClass_AReflectTestActor #if WITH_RELOAD TType* OldSingleton; #endif #if WITH_RELOAD bool bHasChanged; #endif };
这里调用的InnerRegisterFn是前面收集信息的时候,收集的FClassRegisterCompiledInInfo中的AReflectTestActor::StaticClass,我们回看代码生成部分的解析,这里的AReflectTestActor::StaticClass其实就是GetPrivateStaticClassBody()
FClassRegisterCompiledInInfoAReflectTestActorstruct Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics { static constexpr FClassRegisterCompiledInInfo ClassInfo[] = { { Z_Construct_UClass_AReflectTestActor, AReflectTestActor::StaticClass, TEXT("AReflectTestActor"), &Z_Registration_Info_UClass_AReflectTestActor, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AReflectTestActor), 2785921082U) }, }; };
所以UClassRegisterAllCompiledInClasses做的其实就是三件事,初始化了这个类和其super、Outer的StaticClass、加入Package的链表和哈希表、设置了这个类的super和ClassWithin(outer)
void GetPrivateStaticClassBody( const TCHAR* PackageName, //实参:StaticPackage(), //说明:在.generrated.h中DECLARE_CLASS 定义的StaticPackage() const TCHAR* Name, //实参:(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0) //说明:+1去掉前缀比如AActor的A,给准备废弃的类加上 UClass*& ReturnClass, //这个类的注册信息单例Z_Registration_Info_UClass_##TClass.InnerSingleton void(*RegisterNativeFunc)(),//StaticRegisterNatives##TClass,注册函数用 uint32 InSize,//sizeof(TClass),类的大小 uint32 InAlignment,// alignof(TClass),结构体对齐的字节数16/8/4/2/1 EClassFlags InClassFlags,//StaticClassFlags,解释这个类的信息的enum EClassCastFlags InClassCastFlags,//StaticClassCastFlags(), 用于在cast的时候计算类之间关系的位向量 const TCHAR* InConfigName,//配置文件名 UClass::ClassConstructorType InClassConstructor, //__DefaultConstructor UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,//hotreload的时候使用来构造虚函数表,暂时不管 FUObjectCppClassStaticFunctions&& InCppClassStaticFunctions,// GC使用的添加额外引用对象的静态函数指针,若没有定义,则会调用到UObject::AddReferencedObjects,默认函数体为空。 UClass::StaticClassFunctionType InSuperClassFn,//获取基类UClass*的函数指针 UClass::StaticClassFunctionType InWithinClassFn//获取对象外部类UClass*的函数指针,默认是UObject ) { #if WITH_RELOAD //忽略热加载 #endif //使用全局内存分配器获取一块内存,让这个单例指向其地址 ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true); ReturnClass = ::new (ReturnClass)//使用placement new 来在刚刚分配的内存上调用UClass的构造函数,构造了UClass对象(这里应该还未注册反射信息) UClass ( EC_StaticConstructor, Name, InSize, InAlignment, InClassFlags, InClassCastFlags, InConfigName, EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet), InClassConstructor, InClassVTableHelperCtorCaller, MoveTemp(InCppClassStaticFunctions) ); check(ReturnClass); //初始化Super、Outer的Uclass,里面进行与this的关联、注册Package和自身到一个链表和哈希表中 InitializePrivateStaticClass( InSuperClassFn(), ReturnClass, InWithinClassFn(), PackageName, Name ); // Register the class's native functions. RegisterNativeFunc();//StaticRegisterNatives##TClass,注册函数到Class里的TArray<FNativeFunctionLookup> NativeFunctionLookupTable; }
UObjectProcessRegistrants:
主要做的事情是:在DeferredRegister完善UClass对象第一次构造没补充的内容,并放进全局GUObjectArray中和到FUObjectHashTables中
/**
* Process the auto register objects adding them to the UObject array
*/
static void UObjectProcessRegistrants()
{
SCOPED_BOOT_TIMING("UObjectProcessRegistrants");
check(UObjectInitialized());
// Make list of all objects to be registered.
TArray<FPendingRegistrant> PendingRegistrants;
DequeuePendingAutoRegistrants(PendingRegistrants);//将我们之前的那个链表GFirstPendingRegistrant放到这个数组里
for(int32 RegistrantIndex = 0;RegistrantIndex < PendingRegistrants.Num();++RegistrantIndex)
{
const FPendingRegistrant& PendingRegistrant = PendingRegistrants[RegistrantIndex];
UObjectForceRegistration(PendingRegistrant.Object, false);
check(PendingRegistrant.Object->GetClass()); // should have been set by DeferredRegister
// Register may have resulted in new pending registrants being enqueued, so dequeue those.
DequeuePendingAutoRegistrants(PendingRegistrants);
}
}
void UObjectForceRegistration(UObjectBase* Object, bool bCheckForModuleRelease)
{
LLM_SCOPE(ELLMTag::UObject);
//这个就是我们前面构造好的哈希表
TMap<UObjectBase*, FPendingRegistrantInfo>& PendingRegistrants = FPendingRegistrantInfo::GetMap();
FPendingRegistrantInfo* Info = PendingRegistrants.Find(Object);
if (Info)
{
const TCHAR* PackageName = Info->PackageName;
#if USE_PER_MODULE_UOBJECT_BOOTSTRAP
if (bCheckForModuleRelease)
{
UObjectReleaseModuleRegistrants(FName(PackageName));
}
#endif
const TCHAR* Name = Info->Name;
PendingRegistrants.Remove(Object); // delete this first so that it doesn't try to do it twice
Object->DeferredRegister(UClass::StaticClass(),PackageName,Name);
}
}
void UObjectBase::DeferredRegister(UClass *UClassStaticClass,const TCHAR* PackageName,const TCHAR* InName)
{
check(UObjectInitialized());
// Set object properties.
UPackage* Package = CreatePackage(PackageName);//初步构造Package
check(Package);
Package->SetPackageFlags(PKG_CompiledIn);
OuterPrivate = Package;//设定这个UClass的Outer是Package
check(UClassStaticClass);
check(!ClassPrivate);
ClassPrivate = UClassStaticClass;//设定这个UClass的类型,所以就是UClass
//加入全局Object表
// Add to the global object table.
AddObject(FName(InName), EInternalObjectFlags::None);
// At this point all compiled-in objects should have already been fully constructed so it's safe to remove the NotFullyConstructed flag
// which was set in FUObjectArray::AllocateUObjectIndex (called from AddObject)
GUObjectArray.IndexToObject(InternalIndex)->ClearFlags(EInternalObjectFlags::PendingConstruction);
// Make sure that objects disregarded for GC are part of root set.
check(!GUObjectArray.IsDisregardForGC(this) || GUObjectArray.IndexToObject(InternalIndex)->IsRootSet());
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("UObjectBase::DeferredRegister %s %s"), PackageName, InName);
}
void UObjectBase::AddObject(FName InName, EInternalObjectFlags InSetInternalFlags, int32 InInternalIndex, int32 InSerialNumber)
{
NamePrivate = InName;
EInternalObjectFlags InternalFlagsToSet = InSetInternalFlags;
if (!IsInGameThread())
{
InternalFlagsToSet |= EInternalObjectFlags::Async;
}
if (ObjectFlags & RF_MarkAsRootSet)
{
InternalFlagsToSet |= EInternalObjectFlags::RootSet;
ObjectFlags &= ~RF_MarkAsRootSet;
}
if (ObjectFlags & RF_MarkAsNative)
{
InternalFlagsToSet |= EInternalObjectFlags::Native;
ObjectFlags &= ~RF_MarkAsNative;
}
//加入全局UObjArray,GUObjectArray中
GUObjectArray.AllocateUObjectIndex(this, InternalFlagsToSet, InInternalIndex, InSerialNumber);
check(InName != NAME_None && InternalIndex >= 0);
//哈希这个Object,到FUObjectHashTables中
HashObject(this);
check(IsValidLowLevel());
}
UObjectLoadAllCompiledInStructs
/**
* Call StaticStruct for each struct...this sets up the internal singleton, and important works correctly with hot reload
*/
static void UObjectLoadAllCompiledInStructs()
{
SCOPED_BOOT_TIMING("UObjectLoadAllCompiledInStructs");
FEnumDeferredRegistry& EnumRegistry = FEnumDeferredRegistry::Get();
FStructDeferredRegistry& StructRegistry = FStructDeferredRegistry::Get();
{
SCOPED_BOOT_TIMING("UObjectLoadAllCompiledInStructs - CreatePackages (could be optimized!)");
EnumRegistry.DoPendingPackageRegistrations();
StructRegistry.DoPendingPackageRegistrations();
}
// Load Structs
EnumRegistry.DoPendingOuterRegistrations(true);
StructRegistry.DoPendingOuterRegistrations(true);
}
StructRegistry.DoPendingPackageRegistrations()
这里进行Package的创建,还是那一套,在全局object表里找Package,找到了就返回,找不到就创建
/**
* Create all the packages for the packages associated with the registrations
*/
void DoPendingPackageRegistrations()
{
for (int32 Index = ProcessedRegistrations, Num = Registrations.Num(); Index < Num; ++Index)
{
CreatePackage(Registrations[Index].PackageName);
//packageName = TEXT("/Script/ActionRPG")
//在RegisterCompiledInInfo即通过静态结构体构造函数手机反射信息的时候,把packageName存入了Registrations中
}
}
StructRegistry.DoPendingOuterRegistrations(true);
这里进行ScriptStruct即Ustruct反射信息类的创建:
这里就是调用了第二部分提到的收集的静态反射信息
/**
* Invoke the outer registration method for all the registrations
*/
void DoPendingOuterRegistrations(bool UpdateCounter)
{
int32 Num = Registrations.Num();
for (int32 Index = ProcessedRegistrations; Index < Num; ++Index)
{
OuterRegister(Registrations[Index]);//StaticStruct()
}
if (UpdateCounter)
{
ProcessedRegistrations = Num;
}
}
下面是调用链:
class UScriptStruct* FMyStruct::StaticStruct()
{
if (!Z_Registration_Info_UScriptStruct_MyStruct.OuterSingleton)
{
Z_Registration_Info_UScriptStruct_MyStruct.OuterSingleton = GetStaticStruct(Z_Construct_UScriptStruct_FMyStruct, (UObject*)Z_Construct_UPackage__Script_ActionRPG(), TEXT("MyStruct"));
}
return Z_Registration_Info_UScriptStruct_MyStruct.OuterSingleton;
}
class UScriptStruct *GetStaticStruct(class UScriptStruct *(*InRegister)(), UObject* StructOuter, const TCHAR* StructName)
{
UScriptStruct *Result = (*InRegister)();
NotifyRegistrationEvent(*StructOuter->GetOutermost()->GetName(), StructName, ENotifyRegistrationType::NRT_Struct, ENotifyRegistrationPhase::NRP_Finished, nullptr, false, Result);
return Result;
}
UScriptStruct* Z_Construct_UScriptStruct_FMyStruct()
{
if (!Z_Registration_Info_UScriptStruct_MyStruct.InnerSingleton)
{
UECodeGen_Private::ConstructUScriptStruct(Z_Registration_Info_UScriptStruct_MyStruct.InnerSingleton, Z_Construct_UScriptStruct_FMyStruct_Statics::StructParams);
}
return Z_Registration_Info_UScriptStruct_MyStruct.InnerSingleton;
}
ConstructUScriptStruct
下面就是真正构造UScriptStruct的地方:和UClass的构造一一样,OutStruct是返回给外部的引用参数,Params就是生成的反射信息
经历以下步骤:
- Outer的构造
- Super的构造
- UScriptStruct::ICppStructOps的构造,就是自身反射类的构造,调用的是NewStructOps
实际上是(UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FMyStruct>();下方进行分析 - UScriptStruct的构造,传入前面的Outer、super、ICppStructOps、Params中的StructFlags、sizeof、Alignof调用其构造函数构造UScriptStruct,这里就是其区别于Enum的地方:在于其调用了PrepareCppStructOps(),将StructOps中的traits提出并存放到StructFlags中,下方继续分析
- ConstructFProperties:这里和UClass一样,就是构造里面标记的Peoperty,并添加到属性链表中,详细分析看后文
- StaticLink:这里做的事情就是对前文放入ChildProperties的每个Property进行Link,其实就是设定他们大小、Flag,到下面再分门别类,比如包含对象引用的ObjectPtr..、对象析构后需要再析构的、需要内部再次初始化的属性,便于后面日后的序列化、GC、初始化的时候使用,详细分析看后文
- AddMetaData:处理宏标记的meta信息,这里略过(后面补充了Metadata)
void ConstructUScriptStruct(UScriptStruct*& OutStruct, const FStructParams& Params)
{
UObject* (*OuterFunc)() = Params.OuterFunc;
UScriptStruct* (*SuperFunc)() = Params.SuperFunc;
UScriptStruct::ICppStructOps* (*StructOpsFunc)() = (UScriptStruct::ICppStructOps* (*)())Params.StructOpsFunc;
UObject* Outer = OuterFunc ? OuterFunc() : nullptr;
UScriptStruct* Super = SuperFunc ? SuperFunc() : nullptr;
UScriptStruct::ICppStructOps* StructOps = StructOpsFunc ? StructOpsFunc() : nullptr;
if (OutStruct)
{
return;
}
UScriptStruct* NewStruct = new(EC_InternalUseOnlyConstructor, Outer, UTF8_TO_TCHAR(Params.NameUTF8), Params.ObjectFlags) UScriptStruct(FObjectInitializer(), Super, StructOps, (EStructFlags)Params.StructFlags, Params.SizeOf, Params.AlignOf);
OutStruct = NewStruct;
ConstructFProperties(NewStruct, Params.PropertyArray, Params.NumProperties);
NewStruct->StaticLink();
#if WITH_METADATA
AddMetaData(NewStruct, Params.MetaDataArray, Params.NumMetaData);
#endif
}
这是上方用到的参数:StructParams,
//------.gen.cpp中的内容-------
struct Z_Construct_UScriptStruct_FMyStruct_Statics
{
#if WITH_METADATA
static constexpr UECodeGen_Private::FMetaDataPairParam Struct_MetaDataParams[] = {
{ "ModuleRelativePath", "ReflectTestActor.h" },
};
#endif // WITH_METADATA
static void* NewStructOps()
{
return (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FMyStruct>();
}
static const UECodeGen_Private::FStructParams StructParams;
};
const UECodeGen_Private::FStructParams Z_Construct_UScriptStruct_FMyStruct_Statics::StructParams = {
(UObject* (*)())Z_Construct_UPackage__Script_ActionRPG,
nullptr,
&NewStructOps,
"MyStruct",
nullptr,
0,
sizeof(FMyStruct),
alignof(FMyStruct),
RF_Public|RF_Transient|RF_MarkAsNative,
EStructFlags(0x00000001),
METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UScriptStruct_FMyStruct_Statics::Struct_MetaDataParams), Z_Construct_UScriptStruct_FMyStruct_Statics::Struct_MetaDataParams)
};
//-------------------FStructParams的定义-----------------
struct FStructParams
{
UObject* (*OuterFunc)();
UScriptStruct* (*SuperFunc)();
void* (*StructOpsFunc)(); // really returns UScriptStruct::ICppStructOps*
const char* NameUTF8;
const FPropertyParamsBase* const* PropertyArray;
uint16 NumProperties;
uint16 SizeOf;
uint8 AlignOf;
EObjectFlags ObjectFlags;
uint32 StructFlags; // EStructFlags
#if WITH_METADATA
uint16 NumMetaData;
const FMetaDataPairParam* MetaDataArray;
#endif
};
static void* NewStructOps()
{
return (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FMyStruct>();
}
我之前一直在思考,FastArray中的StructOps模板特化到底是干嘛的
template<> struct TStructOpsTypeTraits<FInventoryContainerList> : TStructOpsTypeTraitsBase2<FInventoryContainerList> { enum { WithNetDeltaSerializer = true }; };
这里下意识会以为TStructOpsTypeTraits<FInventoryContainerList>这个类型是跨模块的并疑惑为什么能访问到这个信息,但其实不是的,TStructOpsTypeTraitsBase2这个模板类被定义在Class.h中(模板类随着头文件传播是一种经典的实践,具体行为看符号表与链接),我们在定义自己的TStructOpsTypeTraits的时候,其整个会实例化到我们自己的模块中,任何直接使用到TStructOpsTypeTraits<FInventoryContainerList>的行为,实际上都是在模块内的,所有跨模块的行为都是使用导入的函数举例子:constructUClass是CoreUObjectAPI下的函数,里面使用的StructParams中的参数NewStructOps,就是传递我们模块内部的函数指针NewStructOps。
static void* NewStructOps()
{
return (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FMyStruct>();
}
在这里,讲TStructOpsTypeTraits、UScriptStruct、UScriptStruct中的ICppStructOps、ICppStructOps的派生模板类TCppStructOps全部理清楚:
ICPPStructOps派生TCppStructOps<>这种类派生模板类的方式,
- 统一接口
todo:完善上面的这个模式的理解、补充对下面的解析(TStructOpsTypeTraitsBase2的还没有,大钊文章里有详细解释,好像是Insideue4第十篇)https://zhuanlan.zhihu.com/p/59553490
UScriptStruct::UScriptStruct(const FObjectInitializer& ObjectInitializer, UScriptStruct* InSuperStruct, ICppStructOps* InCppStructOps, EStructFlags InStructFlags, SIZE_T ExplicitSize, SIZE_T ExplicitAlignment )
: UStruct(ObjectInitializer, InSuperStruct, InCppStructOps ? InCppStructOps->GetSize() : ExplicitSize, InCppStructOps ? InCppStructOps->GetAlignment() : ExplicitAlignment )
, StructFlags(EStructFlags(InStructFlags | (InCppStructOps ? STRUCT_Native : STRUCT_NoFlags)))
, bPrepareCppStructOpsCompleted(false)
, CppStructOps(InCppStructOps)
{
PrepareCppStructOps(); // propgate flags, etc
}
/** Look for the CppStructOps if we don't already have it and set the property size **/
void UScriptStruct::PrepareCppStructOps()
{
if (bPrepareCppStructOpsCompleted)
{
return;
}
if (!CppStructOps)
{
//特殊情况,先略过
}
check(!(StructFlags & STRUCT_ComputedFlags));
if (CppStructOps->HasSerializer() || CppStructOps->HasStructuredSerializer())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has a custom serializer."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_SerializeNative );
}
if (CppStructOps->HasPostSerialize())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s wants post serialize."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_PostSerializeNative );
}
if (CppStructOps->HasPostScriptConstruct())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s wants post script construct."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_PostScriptConstruct);
}
if (CppStructOps->HasNetSerializer())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has a custom net serializer."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_NetSerializeNative);
if (CppStructOps->HasNetSharedSerialization())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s can share net serialization."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_NetSharedSerialization);
}
}
if (CppStructOps->HasNetDeltaSerializer())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has a custom net delta serializer."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_NetDeltaSerializeNative);
}
if (CppStructOps->IsPlainOldData())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s is plain old data."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_IsPlainOldData | STRUCT_NoDestructor);
}
else
{
if (CppStructOps->HasCopy())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has a native copy."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_CopyNative);
}
if (!CppStructOps->HasDestructor())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has no destructor."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_NoDestructor);
}
}
if (CppStructOps->HasZeroConstructor())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has zero construction."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_ZeroConstructor);
}
if (CppStructOps->IsPlainOldData() && !CppStructOps->HasZeroConstructor())
{
// hmm, it is safe to see if this can be zero constructed, lets try
int32 Size = CppStructOps->GetSize();
uint8* TestData00 = (uint8*)FMemory::Malloc(Size);
FMemory::Memzero(TestData00,Size);
CppStructOps->Construct(TestData00);
CppStructOps->Construct(TestData00); // slightly more like to catch "internal counters" if we do this twice
bool IsZeroConstruct = true;
for (int32 Index = 0; Index < Size && IsZeroConstruct; Index++)
{
if (TestData00[Index])
{
IsZeroConstruct = false;
}
}
FMemory::Free(TestData00);
if (IsZeroConstruct)
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has DISCOVERED zero construction. Size = %d"),*GetName(), Size);
StructFlags = EStructFlags(StructFlags | STRUCT_ZeroConstructor);
}
}
if (CppStructOps->HasIdentical())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native identical."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_IdenticalNative);
}
if (CppStructOps->HasAddStructReferencedObjects())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native AddStructReferencedObjects."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_AddStructReferencedObjects);
}
if (CppStructOps->HasExportTextItem())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native ExportTextItem."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_ExportTextItemNative);
}
if (CppStructOps->HasImportTextItem())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native ImportTextItem."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_ImportTextItemNative);
}
if (CppStructOps->HasSerializeFromMismatchedTag() || CppStructOps->HasStructuredSerializeFromMismatchedTag())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native SerializeFromMismatchedTag."),*GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_SerializeFromMismatchedTag);
}
#if WITH_EDITOR
if (CppStructOps->HasCanEditChange())
{
UE_LOG(LogClass, Verbose, TEXT("Native struct %s has native CanEditChange."), *GetName());
StructFlags = EStructFlags(StructFlags | STRUCT_CanEditChange);
}
#endif
check(!bPrepareCppStructOpsCompleted); // recursion is unacceptable
bPrepareCppStructOpsCompleted = true;
}
todo:PrepareCppStructOps中有一段是“//特殊情况,先略过”,当时我忽略了,现在想来,这里大概是和动态构造有关,UScriptStruct和UClass一样,不只是native用来承载对应反射数据的,在运行时也有构造的需求,但是我不太清楚是什么需求需要用到,查看了一下UDataTable好像会用到。
UClass据我所知,在动态使用的是派生的blueprintGeneratedClass,作为蓝图的动态信息的承载,但是也没细看
搞懂这个有助于深入理解动态类型在ue中的需求、真正理解UScriptStruct
不过似乎和我一开始想了解的:TCppOps和ICppOps这个架构和Tdelegate、IDelegate关系不太大
唯一有关系的地方就是TCppOps的收集第二部分,通过静态结构体构造函数收集反射信息(main之前)
for (size_t Index = 0; Index < NumStructInfo; ++Index) { const FStructRegisterCompiledInInfo& Info = StructInfo[Index]; RegisterCompiledInInfo(Info.OuterRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo); if (Info.CreateCppStructOps != nullptr) { UScriptStruct::DeferCppStructOps(FTopLevelAssetPath(FName(PackageName), FName(Info.Name)), (UScriptStruct::ICppStructOps*)Info.CreateCppStructOps()); } }这里之前根本没分析,因为确实和native无关

> UnrealEditor-CoreUObject.dll!TSet<TTuple<FTopLevelAssetPath,UScriptStruct::ICppStructOps *>,TDefaultMapHashableKeyFuncs<FTopLevelAssetPath,UScriptStruct::ICppStructOps *,0>,FDefaultSetAllocator>::Emplace<TPairInitializer<FTopLevelAssetPath const &,UScriptStruct::ICppStructOps * const &>>(TPairInitializer<FTopLevelAssetPath const &,UScriptStruct::ICppStructOps * const &> && Args, bool * bIsAlreadyInSetPtr) 行 738 C++
[内联框架] UnrealEditor-CoreUObject.dll!TMapBase<FTopLevelAssetPath,UScriptStruct::ICppStructOps *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FTopLevelAssetPath,UScriptStruct::ICppStructOps *,0>>::Emplace(const FTopLevelAssetPath &) 行 433 C++
[内联框架] UnrealEditor-CoreUObject.dll!TMapBase<FTopLevelAssetPath,UScriptStruct::ICppStructOps *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FTopLevelAssetPath,UScriptStruct::ICppStructOps *,0>>::Add(const FTopLevelAssetPath &) 行 391 C++
UnrealEditor-CoreUObject.dll!UScriptStruct::DeferCppStructOps(FTopLevelAssetPath Target, UScriptStruct::ICppStructOps * InCppStructOps) 行 2759 C++
UnrealEditor-CoreUObject.dll!RegisterCompiledInInfo(const wchar_t * PackageName, const FClassRegisterCompiledInInfo * ClassInfo, unsigned __int64 NumClassInfo, const FStructRegisterCompiledInInfo * StructInfo, unsigned __int64 NumStructInfo, const FEnumRegisterCompiledInInfo * EnumInfo, unsigned __int64 NumEnumInfo) 行 649 C++
UnrealEditor-ActionRPG-Win64-DebugGame.dll!`dynamic initializer for 'Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_2038008627''() 行 266 C++
ucrtbase.dll!00007ff86b3ce473() 未知
UnrealEditor-ActionRPG-Win64-DebugGame.dll!dllmain_crt_process_attach(HINSTANCE__ * const instance, void * const reserved) 行 66 C++
UnrealEditor-ActionRPG-Win64-DebugGame.dll!dllmain_dispatch(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) 行 276 C++
ntdll.dll!00007ff86da49a1d() 未知
ntdll.dll!00007ff86da9d2f7() 未知
ntdll.dll!00007ff86da9d08a() 未知
ntdll.dll!00007ff86da6d947() 未知
ntdll.dll!00007ff86da4fbae() 未知
ntdll.dll!00007ff86da473e4() 未知
ntdll.dll!00007ff86da46af4() 未知
KernelBase.dll!00007ff86b511ea2() 未知
[内联框架] UnrealEditor-Core.dll!FWindowsPlatformProcess::LoadLibraryWithSearchPaths::__l2::<lambda_2>::operator()() 行 2244 C++
UnrealEditor-Core.dll!FWindowsPlatformProcess::LoadLibraryWithSearchPaths(const FString & FileName, const TArray<FString,TSizedDefaultAllocator<32>> & SearchPaths) 行 2238 C++
UnrealEditor-Core.dll!FWindowsPlatformProcess::GetDllHandle(const wchar_t * FileName) 行 162 C++
UnrealEditor-Core.dll!FModuleManager::LoadModuleWithFailureReason(const FName InModuleName, EModuleLoadResult & OutFailureReason, ELoadModuleFlags InLoadModuleFlags) 行 631 C++
UnrealEditor-Projects.dll!FModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase, const TArray<FModuleDescriptor,TSizedDefaultAllocator<32>> & Modules, TMap<FName,enum EModuleLoadResult,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FName,enum EModuleLoadResult,0>> & ModuleLoadErrors) 行 696 C++
UnrealEditor-Projects.dll!FProjectManager::LoadModulesForProject(const ELoadingPhase::Type LoadingPhase) 行 62 C++
UnrealEditor-Win64-DebugGame.exe!FEngineLoop::LoadStartupModules() 行 4738 C++
UnrealEditor-Win64-DebugGame.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine) 行 4000 C++
[内联框架] UnrealEditor-Win64-DebugGame.exe!FEngineLoop::PreInit(const wchar_t *) 行 4483 C++
[内联框架] UnrealEditor-Win64-DebugGame.exe!EnginePreInit(const wchar_t *) 行 41 C++
UnrealEditor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine) 行 136 C++
UnrealEditor-Win64-DebugGame.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) 行 247 C++
UnrealEditor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) 行 298 C++
[内联框架] UnrealEditor-Win64-DebugGame.exe!invoke_main() 行 102 C++
UnrealEditor-Win64-DebugGame.exe!__scrt_common_main_seh() 行 288 C++
kernel32.dll!00007ff86ca37374() 未知
ntdll.dll!00007ff86da7cc91() 未知
UEnum
TextureDefines.h中的ETextureSourceCompressionFormat为例子
UENUM()
enum ETextureSourceCompressionFormat : int
{
TSCF_None UMETA(DisplayName = "None"),
TSCF_PNG UMETA(DisplayName = "PNG"),
TSCF_JPEG UMETA(DisplayName = "JPEG"),
TSCF_UEJPEG UMETA(DisplayName = "UE JPEG"),
TSCF_MAX
};
以Texture.h中的TEnumAsByte<enum ETextureSourceCompressionFormat> CompressionFormat;为例子

const UECodeGen_Private::FBytePropertyParams Z_Construct_UScriptStruct_FTextureSource_Statics::NewProp_CompressionFormat = { "CompressionFormat", nullptr, (EPropertyFlags)0x0040000800020001, UECodeGen_Private::EPropertyGenFlags::Byte, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(FTextureSource, CompressionFormat), Z_Construct_UEnum_Engine_ETextureSourceCompressionFormat, METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_CompressionFormat_MetaData), NewProp_CompressionFormat_MetaData) }; // 1327261017
UObjectLoadAllCompiledInDefaultProperties:
这里是一个很重要的地方,里调用了反射生成的OuterSingleton(还是StaticClass)构造函数为UClass*们继续构造和创建类默认对象(CDO)
/**
* Load any outstanding compiled in default properties
*/
static void UObjectLoadAllCompiledInDefaultProperties(TArray<UClass*>& OutAllNewClasses)
{
TRACE_LOADTIME_REQUEST_GROUP_SCOPE(TEXT("UObjectLoadAllCompiledInDefaultProperties"));
static FName LongEnginePackageName(TEXT("/Script/Engine"));
FClassDeferredRegistry& ClassRegistry = FClassDeferredRegistry::Get();
if (ClassRegistry.HasPendingRegistrations())
{
SCOPED_BOOT_TIMING("UObjectLoadAllCompiledInDefaultProperties");
TArray<UClass*> NewClasses;//存放Packge为TEXT("/Script/ActionRPG")的UClass指针,就是我们项目的PackageName
TArray<UClass*> NewClassesInCoreUObject;//存放Package为"/Script/CoreUObject"的UClass指针
TArray<UClass*> NewClassesInEngine;//存放Package为"/Script/Engine"的UClass指针
//这里会调用.gen生成的构造函数Z_Construct_UClass_AReflectTestActor()来继续构造我们的UClass,把返回的UClass放入对应的TArray中,下面逐个构造CDO
ClassRegistry.DoPendingOuterRegistrations(true, [&OutAllNewClasses, &NewClasses, &NewClassesInCoreUObject, &NewClassesInEngine](const TCHAR* PackageName, UClass& Class) -> void
{
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("UObjectLoadAllCompiledInDefaultProperties After Registrant %s %s"), PackageName, *Class.GetName());
if (Class.GetOutermost()->GetFName() == GLongCoreUObjectPackageName)
{
NewClassesInCoreUObject.Add(&Class);
}
else if (Class.GetOutermost()->GetFName() == LongEnginePackageName)
{
NewClassesInEngine.Add(&Class);
}
else
{
NewClasses.Add(&Class);
}
OutAllNewClasses.Add(&Class);//所有UClass都要加进来
});
//.......第二次分析:
auto NotifyClassFinishedRegistrationEvents = [](TArray<UClass*>& Classes)
{
for (UClass* Class : Classes)
{
TCHAR PackageName[FName::StringBufferSize];
TCHAR ClassName[FName::StringBufferSize];
Class->GetOutermost()->GetFName().ToString(PackageName);
Class->GetFName().ToString(ClassName);
NotifyRegistrationEvent(PackageName, ClassName, ENotifyRegistrationType::NRT_Class, ENotifyRegistrationPhase::NRP_Finished, nullptr, false, Class);
}
};
// notify async loader of all new classes before creating the class default objects
{
SCOPED_BOOT_TIMING("NotifyClassFinishedRegistrationEvents");
NotifyClassFinishedRegistrationEvents(NewClassesInCoreUObject);
NotifyClassFinishedRegistrationEvents(NewClassesInEngine);
NotifyClassFinishedRegistrationEvents(NewClasses);
}
{
SCOPED_BOOT_TIMING("CoreUObject Classes");
for (UClass* Class : NewClassesInCoreUObject) // we do these first because we assume these never trigger loads
{
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject Begin %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
Class->GetDefaultObject();
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject End %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
}
}
{
SCOPED_BOOT_TIMING("Engine Classes");
for (UClass* Class : NewClassesInEngine) // we do these second because we want to bring the engine up before the game
{
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject Begin %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
Class->GetDefaultObject();
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject End %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
}
}
{
SCOPED_BOOT_TIMING("Other Classes");
for (UClass* Class : NewClasses)
{
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject Begin %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
Class->GetDefaultObject();//这里是创建我们类的CDO的入口
UE_LOG(LogUObjectBootstrap, Verbose, TEXT("GetDefaultObject End %s %s"), *Class->GetOutermost()->GetName(), *Class->GetName());
}
}
//下面好像也是GC相关的
FFeedbackContext& ErrorsFC = UClass::GetDefaultPropertiesFeedbackContext();
if (ErrorsFC.GetNumErrors() || ErrorsFC.GetNumWarnings())
{
TArray<FString> AllErrorsAndWarnings;
ErrorsFC.GetErrorsAndWarningsAndEmpty(AllErrorsAndWarnings);
FString AllInOne;
UE_LOG(LogUObjectBase, Warning, TEXT("-------------- Default Property warnings and errors:"));
for (const FString& ErrorOrWarning : AllErrorsAndWarnings)
{
UE_LOG(LogUObjectBase, Warning, TEXT("%s"), *ErrorOrWarning);
AllInOne += ErrorOrWarning;
AllInOne += TEXT("\n");
}
FMessageDialog::Open(EAppMsgType::Ok, FText::Format( NSLOCTEXT("Core", "DefaultPropertyWarningAndErrors", "Default Property warnings and errors:\n{0}"), FText::FromString( AllInOne ) ) );
}
}
}
DoPendingOuterRegistrations:
这里会调用.gen生成的构造函数Z_Construct_UClass_AReflectTestActor()来继续构造我们的UClass,把返回的UClass放入对应的TArray中,下面再逐个构造CDO
/**
* Invoke the outer registration method for all the registrations and invoke the given function with the resulting object
*/
template <typename FuncType>
void DoPendingOuterRegistrations(bool UpdateCounter, FuncType&& InOnRegistration)
{
int32 Num = Registrations.Num();
for (int32 Index = ProcessedRegistrations; Index < Num; ++Index)
{
TType* Object = OuterRegister(Registrations[Index]);
InOnRegistration(Registrations[Index].PackageName, *Object);
}
if (UpdateCounter)
{
ProcessedRegistrations = Num;
}
}
ConstructUClass:
OuterRegister 会调用Z_Construct_UClass_AReflectTestActor(),最终调用到ConstructUClass
UClass* Z_Construct_UClass_AReflectTestActor()
{
//OuterSingleton依然是StaticClass
if (!Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton)
{
UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton, Z_Construct_UClass_AReflectTestActor_Statics::ClassParams);
}
return Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton;
}
下面是ConstructUClass核心
//@@@ OutClass = Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton
//@@@ Params = Z_Construct_UClass_AReflectTestActor_Statics::ClassParams
void ConstructUClass(UClass*& OutClass, const FClassParams& Params)
{
if (OutClass && (OutClass->ClassFlags & CLASS_Constructed))
{
return;//如果已经构造了就直接返回,通过判NULL和Flag来判断是否已经构造
}
for (UObject* (*const *SingletonFunc)() = Params.DependencySingletonFuncArray, *(*const *SingletonFuncEnd)() = SingletonFunc + Params.NumDependencySingletons; SingletonFunc != SingletonFuncEnd; ++SingletonFunc)
{
(*SingletonFunc)();//调用依赖项的构造函数,是和这个一样的ConstructUClass,在我这里的例子是:
/*
(UObject* (*)())Z_Construct_UClass_AActor,我们的Actor依赖于AActor
(UObject* (*)())Z_Construct_UPackage__Script_ActionRPG,依赖于Package
*/
}
UClass* NewClass = Params.ClassNoRegisterFunc();//这里调用的就是inner的StaticClass,构造好了的会直接返回(没经过前面流程的需要调用)
OutClass = NewClass;
//防止再次构造
if (NewClass->ClassFlags & CLASS_Constructed)
{
return;
}
UObjectForceRegistration(NewClass);//前面调用过一次,就是把Uclass放到全局obj表里(没经过前面流程的需要调用)
UClass* SuperClass = NewClass->GetSuperClass();//这里返回的就是SuperStruct在innerConstruct中设置过,无论是前面流程UClassRegisterAllCompiledInClasses调用过STaticClass还是在这里调用过ClassNoRegisterFunc()的StaticClass,到了这一步,都调用过了StaticClass
if (SuperClass)
{
NewClass->ClassFlags |= (SuperClass->ClassFlags & CLASS_Inherit);
}
NewClass->ClassFlags |= (EClassFlags)(Params.ClassFlags | CLASS_Constructed);
// Make sure the reference token stream is empty since it will be reconstructed later on
// This should not apply to intrinsic classes since they emit native references before AssembleReferenceTokenStream is called.
if ((NewClass->ClassFlags & CLASS_Intrinsic) != CLASS_Intrinsic)
{
check((NewClass->ClassFlags & CLASS_TokenStreamAssembled) != CLASS_TokenStreamAssembled);
NewClass->ReferenceSchema.Reset();//gc相关
}
//下面分析
NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);
//下面分析
ConstructFProperties(NewClass, Params.PropertyArray, Params.NumProperties);
if (Params.ClassConfigNameUTF8)
{
//ClassConfigNameUTF8 = "Engine"
NewClass->ClassConfigName = FName(UTF8_TO_TCHAR(Params.ClassConfigNameUTF8));
}
//@@@ CppClassInfo = static constexpr FCppClassTypeInfoStatic StaticCppClassTypeInfo = {
// TCppClassTypeTraits<AReflectTestActor>::IsAbstract,
//};
NewClass->SetCppTypeInfoStatic(Params.CppClassInfo);// 设置是否为虚类
//这里是对interface的,先掠过
if (int32 NumImplementedInterfaces = Params.NumImplementedInterfaces)
{
NewClass->Interfaces.Reserve(NumImplementedInterfaces);
for (const FImplementedInterfaceParams* ImplementedInterface = Params.ImplementedInterfaceArray, *ImplementedInterfaceEnd = ImplementedInterface + NumImplementedInterfaces; ImplementedInterface != ImplementedInterfaceEnd; ++ImplementedInterface)
{
UClass* (*ClassFunc)() = ImplementedInterface->ClassFunc;
UClass* InterfaceClass = ClassFunc ? ClassFunc() : nullptr;
NewClass->Interfaces.Emplace(InterfaceClass, ImplementedInterface->Offset, ImplementedInterface->bImplementedByK2);
}
}
#if WITH_METADATA
AddMetaData(NewClass, Params.MetaDataArray, Params.NumMetaData);
#endif
NewClass->StaticLink();//下面进行独立分析
NewClass->SetSparseClassDataStruct(NewClass->GetSparseClassDataArchetypeStruct());//SparseClassDataStruct是一个UClass的一个实验性新功能,如果一些属性不会在实例化后更改,比如他是一个ReadOnly的,那么就可以放在这里,作为常量,类似字符串的短字符串优化
}
ConstructUFunction & CreateLinkAndAddChildFunctionsToMap
CreateLinkAndAddChildFunctionsToMap:这个函数干的就是构造好UClass里的FuncMap
其实就是根据不同的情况创建UFunction对象,然后Bind(绑定函数指针),Link,然后放到了FuncMap当中
// /** Map of all functions by name contained in this class */
//TMap<FName, TObjectPtr<UFunction>> FuncMap;
//@@@ FunctionLinkArray = static constexpr FClassFunctionLinkInfo FuncInfo[] = {
// { &Z_Construct_UFunction_AReflectTestActor_AddSpeed, "AddSpeed" }, // 2567349281
//};
//@@@ NumFunctions = UE_ARRAY_COUNT(FuncInfo),
void UClass::CreateLinkAndAddChildFunctionsToMap(const FClassFunctionLinkInfo* Functions, uint32 NumFunctions)
{
for (; NumFunctions; --NumFunctions, ++Functions)
{
const char* FuncNameUTF8 = Functions->FuncNameUTF8;
UFunction* Func = Functions->CreateFuncPtr();
Func->Next = Children;
Children = Func;
AddFunctionToFunctionMap(Func, FName(UTF8_TO_TCHAR(FuncNameUTF8)));
}
}
/** Add a function to the function map */
void AddFunctionToFunctionMap(UFunction* Function, FName FuncName)
{
{
FUClassFuncScopeWriteLock ScopeLock(FuncMapLock);
FuncMap.Add(FuncName, Function);
}
{
// Remove from the function cache if it exists
FUClassFuncScopeWriteLock ScopeLock(AllFunctionsCacheLock);
AllFunctionsCache.Remove(FuncName);
}
}
//--------------------------插入FClassFunctionLinkInfo的定义
struct FClassFunctionLinkInfo
{
UFunction* (*CreateFuncPtr)();
const char* FuncNameUTF8;
};
//-------------------------------------------------------------------
//--------------------------插入gen.cpp中生成的内容---------------------
UFunction* Z_Construct_UFunction_AReflectTestActor_AddSpeed()
{
static UFunction* ReturnFunction = nullptr;
if (!ReturnFunction)
{
UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::FuncParams);
}
return ReturnFunction;
}
------------------------------------------------------------------------
//Functions的调用会最终来到这里
FORCEINLINE void ConstructUFunctionInternal(UFunction*& OutFunction, const FFunctionParams& Params, UFunction** SingletonPtr)
{
UObject* (*OuterFunc)() = Params.OuterFunc;
UFunction* (*SuperFunc)() = Params.SuperFunc;
UObject* Outer = OuterFunc ? OuterFunc() : nullptr;
UFunction* Super = SuperFunc ? SuperFunc() : nullptr;
if (OutFunction)
{
return;
}
FName FuncName(UTF8_TO_TCHAR(Params.NameUTF8));
#if WITH_LIVE_CODING
//这里先不管
#endif
UFunction* NewFunction;
if (Params.FunctionFlags & FUNC_Delegate)
{
if (Params.OwningClassName == nullptr)
{
NewFunction = new (EC_InternalUseOnlyConstructor, Outer, FuncName, Params.ObjectFlags) UDelegateFunction(
FObjectInitializer(),
Super,
Params.FunctionFlags,
Params.StructureSize
);
}
else
{
USparseDelegateFunction* NewSparseFunction = new (EC_InternalUseOnlyConstructor, Outer, FuncName, Params.ObjectFlags) USparseDelegateFunction(
FObjectInitializer(),
Super,
Params.FunctionFlags,
Params.StructureSize
);
NewSparseFunction->OwningClassName = FName(Params.OwningClassName);
NewSparseFunction->DelegateName = FName(Params.DelegateName);
NewFunction = NewSparseFunction;
}
}
else
{
NewFunction = new (EC_InternalUseOnlyConstructor, Outer, FuncName, Params.ObjectFlags) UFunction(
FObjectInitializer(),
Super,
Params.FunctionFlags,
Params.StructureSize
);
}
OutFunction = NewFunction;
#if WITH_LIVE_CODING
NewFunction->SingletonPtr = SingletonPtr;
if (NewFunction == PrevFunction)
{
NewFunction->Next = PrevFunctionNextField;
}
#endif
#if WITH_METADATA
AddMetaData(NewFunction, Params.MetaDataArray, Params.NumMetaData);
#endif
NewFunction->RPCId = Params.RPCId; //设置RPCId
NewFunction->RPCResponseId = Params.RPCResponseId;//设置RPCResponseId
//把返回值和参数,像构建Property一样,构造后init,放入UFunction(UFunction继承自UStruct)中
ConstructFProperties(NewFunction, Params.PropertyArray, Params.NumProperties);
//绑定这个UFunction到对应函数指针
NewFunction->Bind();
NewFunction->StaticLink();
}
/*Bind就是绑定UFUnction内部的Func到都是一样签名的DECLARE_FUNCTION定义的函数中,
蓝图调用函数会经过void UObject::ProcessEvent( UFunction* Function, void* Parms )
UFunction::Invoke{return (*Func)(Obj, Stack, RESULT_PARAM);}
最终来到这里绑定的函数入口
*/
void UFunction::Bind()
{
UClass* OwnerClass = GetOwnerClass();
// if this isn't a native function, or this function belongs to a native interface class (which has no C++ version),
// use ProcessInternal (call into script VM only) as the function pointer for this function
// 不是Native函数则绑定ProcessInternal地址,UObject中DECLARE_FUNCTION(ProcessInternal)
if (!HasAnyFunctionFlags(FUNC_Native))
{
// Use processing function.
Func = &UObject::ProcessInternal;
}
else
{
// 是Native函数则从NativeFunctionLookupTable表中查询指针便规定,这里再STaticClass中的最后把生成的exec函数都放入了NativeFunctionLookupTable中。
// Find the function in the class's native function lookup table.
FName Name = GetFName();
FNativeFunctionLookup* Found = OwnerClass->NativeFunctionLookupTable.FindByPredicate([=](const FNativeFunctionLookup& NativeFunctionLookup){ return Name == NativeFunctionLookup.Name; });
if (Found)
{
Func = Found->Pointer;
}
#if USE_COMPILED_IN_NATIVES
else if (!HasAnyFunctionFlags(FUNC_NetRequest))
{
UE_LOG(LogClass, Warning,TEXT("Failed to bind native function %s.%s"),*OwnerClass->GetName(),*GetName());
}
#endif
}
}
//UObject::ProcessInternal:
DEFINE_FUNCTION(UObject::ProcessInternal)
{
#if DO_BLUEPRINT_GUARD
// remove later when stable
if (P_THIS->GetClass()->HasAnyClassFlags(CLASS_NewerVersionExists))
{
if (!GIsReinstancing)
{
ensureMsgf(!P_THIS->GetClass()->HasAnyClassFlags(CLASS_NewerVersionExists), TEXT("Object '%s' is being used for execution, but its class is out of date and has been replaced with a recompiled class!"), *P_THIS->GetFullName());
}
return;
}
#endif
UFunction* Function = (UFunction*)Stack.Node;
int32 FunctionCallspace = P_THIS->GetFunctionCallspace(Function, NULL);
if (FunctionCallspace & FunctionCallspace::Remote)
{
P_THIS->CallRemoteFunction(Function, Stack.Locals, Stack.OutParms, NULL);
}
if (FunctionCallspace & FunctionCallspace::Local)
{
ProcessLocalScriptFunction(Context, Stack, RESULT_PARAM);
}
else
{
FProperty* ReturnProp = (Function)->GetReturnProperty();
ClearReturnValue(ReturnProp, RESULT_PARAM);
}
}
ConstructFProperties:
就是根据FProperty类型来new一个对应的对象,什么FintProperty自己的构造函数体都是空的,FProperty干完了,在FProperty构造函数中有绑定到Outer的操作(init)、还有设置其到开头地址的Offset的操作
细节:
- FxxxxPropertyParams:
在存储的时候,不同FProperty的FxxxxPropertyParams*靠强制类型转换,统一到FPropertyParamsBase*(类型擦除)来存储到容器中(因为这里只是要统一的指针类型来存储,所以貌似用void*都行)
在使用的时候,再靠模板类型,进行强制类型转换实现静态多态
![image-20250523221751932]()
//const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed = { "Speed", nullptr, (EPropertyFlags)0x0010000000000000, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(AReflectTestActor, Speed), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_Speed_MetaData), NewProp_Speed_MetaData) };
//const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Health = { "Health", nullptr, (EPropertyFlags)0x0010000000000020, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(AReflectTestActor, Health), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_Health_MetaData), NewProp_Health_MetaData) };
//@@@ PropertyArray = const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AReflectTestActor_Statics::PropPointers[] = {
// (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed,
// (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Health,
//};
//@@@ NumProperties = UE_ARRAY_COUNT (Z_Construct_UClass_AReflectTestActor_Statics::PropPointers)
void ConstructFProperties(UObject* Outer, const FPropertyParamsBase* const* PropertyArray, int32 NumProperties)
{
// Move pointer to the end, because we'll iterate backwards over the properties
PropertyArray += NumProperties;
while (NumProperties)
{
ConstructFProperty(Outer, PropertyArray, NumProperties);
}
}
void ConstructFProperty(FFieldVariant Outer, const FPropertyParamsBase* const*& PropertyArray, int32& NumProperties)
{
const FPropertyParamsBase* PropBase = *--PropertyArray;
uint32 ReadMore = 0;
FProperty* NewProp = nullptr;
switch (PropBase->Flags & PropertyTypeMask)
{
default:
{
// Unsupported property type
check(false);
}
case EPropertyGenFlags::Byte:
{
NewProp = NewFProperty<FByteProperty, FBytePropertyParams>(Outer, *PropBase);
}
break;
case EPropertyGenFlags::Int8:
{
NewProp = NewFProperty<FInt8Property, FInt8PropertyParams>(Outer, *PropBase);
}
break;
//........................一堆case
const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed = { "Speed", nullptr, (EPropertyFlags)0x0010000000000000, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(AReflectTestActor, Speed), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_Speed_MetaData), NewProp_Speed_MetaData) }; const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Health = { "Health", nullptr, (EPropertyFlags)0x0010000000000020, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(AReflectTestActor, Health), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_Health_MetaData), NewProp_Health_MetaData) }; const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AReflectTestActor_Statics::PropPointers[] = { (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed, (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Health, }; static_assert(UE_ARRAY_COUNT(Z_Construct_UClass_AReflectTestActor_Statics::PropPointers) < 2048);本文例子生存的FProperty相关的反射信息
NewFProperty最终会来到FProperty构造函数中

FProperty::FProperty(FFieldVariant InOwner, const UECodeGen_Private::FPropertyParamsBaseWithOffset& Prop, EPropertyFlags AdditionalPropertyFlags /*= CPF_None*/)
: FField(InOwner, UTF8_TO_TCHAR(Prop.NameUTF8), Prop.ObjectFlags)
, ArrayDim(1)
, ElementSize(0)
, PropertyFlags(Prop.PropertyFlags | AdditionalPropertyFlags)
, RepIndex(0)
, BlueprintReplicationCondition(COND_None)
, Offset_Internal(0)
, PropertyLinkNext(nullptr)
, NextRef(nullptr)
, DestructorLinkNext(nullptr)
, PostConstructLinkNext(nullptr)
{
this->Offset_Internal = Prop.Offset;
//这里就是用反射生成的STRUCT_OFFSET(AReflectTestActor, Health)算出的一个Property的Offset
//只有FPropertyParamsBaseWithOffset类型才会设置offset,
// struct FGenericPropertyParams // : FPropertyParamsBaseWithOffset float、int使用的都是FGenericPropertyParams的别名
//FGenericPropertyParams就是FPropertyParamsBaseWithOffset
Init();
}
FProperty::FProperty(FFieldVariant InOwner, const UECodeGen_Private::FPropertyParamsBaseWithoutOffset& Prop, EPropertyFlags AdditionalPropertyFlags /*= CPF_None*/)
: FField(InOwner, UTF8_TO_TCHAR(Prop.NameUTF8), Prop.ObjectFlags)
, ArrayDim(1)
, ElementSize(0)
, PropertyFlags(Prop.PropertyFlags | AdditionalPropertyFlags)
, RepIndex(0)
, BlueprintReplicationCondition(COND_None)
, Offset_Internal(0)
, PropertyLinkNext(nullptr)
, NextRef(nullptr)
, DestructorLinkNext(nullptr)
, PostConstructLinkNext(nullptr)
{
Init();
}
void FProperty::Init()
{
#if !WITH_EDITORONLY_DATA
//@todo.COOKER/PACKAGER: Until we have a cooker/packager step, this can fire when WITH_EDITORONLY_DATA is not defined!
// checkSlow(!HasAnyPropertyFlags(CPF_EditorOnly));
#endif // WITH_EDITORONLY_DATA
checkSlow(GetOwnerUField()->HasAllFlags(RF_Transient));
checkSlow(HasAllFlags(RF_Transient));
if (GetOwner<UObject>())
{
UField* OwnerField = GetOwnerChecked<UField>();
OwnerField->AddCppProperty(this);
}
else
{
FField* OwnerField = GetOwnerChecked<FField>();
OwnerField->AddCppProperty(this);
}
}
//重载AddCppProperty
//Property是如何加入UStruct链表的:
void UStruct::AddCppProperty(FProperty* Property)
{
Property->Next = ChildProperties;
ChildProperties = Property;
}
void FArrayProperty::AddCppProperty(FProperty* Property)
{
Inner = Property; //元素属性
}
void FMapProperty::AddCppProperty(FProperty* Property)
{
if (!KeyProp) {KeyProp = Property;}//第一个是键属性
else {ValueProp = Property;}//第二个是值属性
}
void FSetProperty::AddCppProperty(FProperty* Property)
{
ElementProp = Property;//元素属性
}
void FEnumProperty::AddCppProperty(FProperty* Inner)
{
UnderlyingProp = CastChecked<UNumericProperty>(Inner);//依靠的整数属性
}

// Offset of a struct member.
#ifdef __clang__
#define STRUCT_OFFSET( struc, member ) __builtin_offsetof(struc, member)
#else
#define STRUCT_OFFSET( struc, member ) offsetof(struc, member)
#endif
#define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
//这个offsetof的解释:
/*
其基本可以等同于 ((size_t)&((s*)0)->m)
原理就是把0地址转为s*然后让其指向m对象,然后取地址,这样就得到了m对象对于s开头的offset(偏移量)
*/
下面是.gen.cpp中生成的Z_Construct_UClass_AReflectTestActor相关内容
static const UECodeGen_Private::FFloatPropertyParams NewProp_Speed; static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[]; static UObject* (*const DependentSingletons[])(); static constexpr FClassFunctionLinkInfo FuncInfo[] = { { &Z_Construct_UFunction_AReflectTestActor_AddSpeed, "AddSpeed" }, // 2567349281 }; static_assert(UE_ARRAY_COUNT(FuncInfo) < 2048); static constexpr FCppClassTypeInfoStatic StaticCppClassTypeInfo = { TCppClassTypeTraits<AReflectTestActor>::IsAbstract, }; static const UECodeGen_Private::FClassParams ClassParams; }; const UECodeGen_Private::FFloatPropertyParams Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed = { "Speed", nullptr, (EPropertyFlags)0x0010000000000000, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(AReflectTestActor, Speed), METADATA_PARAMS(UE_ARRAY_COUNT(NewProp_Speed_MetaData), NewProp_Speed_MetaData) }; const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AReflectTestActor_Statics::PropPointers[] = { (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AReflectTestActor_Statics::NewProp_Speed, }; static_assert(UE_ARRAY_COUNT(Z_Construct_UClass_AReflectTestActor_Statics::PropPointers) < 2048); UObject* (*const Z_Construct_UClass_AReflectTestActor_Statics::DependentSingletons[])() = { (UObject* (*)())Z_Construct_UClass_AActor, (UObject* (*)())Z_Construct_UPackage__Script_ActionRPG, }; static_assert(UE_ARRAY_COUNT(Z_Construct_UClass_AReflectTestActor_Statics::DependentSingletons) < 16); //---------------------这里补上定义------------------- struct FClassParams { UClass* (*ClassNoRegisterFunc)(); //&AReflectTestActor::StaticClass, const char* ClassConfigNameUTF8; const FCppClassTypeInfoStatic* CppClassInfo; UObject* (*const *DependencySingletonFuncArray)(); const FClassFunctionLinkInfo* FunctionLinkArray; const FPropertyParamsBase* const* PropertyArray; const FImplementedInterfaceParams* ImplementedInterfaceArray; uint32 NumDependencySingletons : 4; uint32 NumFunctions : 11; uint32 NumProperties : 11; uint32 NumImplementedInterfaces : 6; uint32 ClassFlags; // EClassFlags #if WITH_METADATA uint16 NumMetaData; const FMetaDataPairParam* MetaDataArray; #endif }; //------------------------------------------------ const UECodeGen_Private::FClassParams Z_Construct_UClass_AReflectTestActor_Statics::ClassParams = { &AReflectTestActor::StaticClass, "Engine", &StaticCppClassTypeInfo, DependentSingletons, FuncInfo, Z_Construct_UClass_AReflectTestActor_Statics::PropPointers, nullptr, UE_ARRAY_COUNT(DependentSingletons), UE_ARRAY_COUNT(FuncInfo), UE_ARRAY_COUNT(Z_Construct_UClass_AReflectTestActor_Statics::PropPointers), 0, 0x009000A4u, METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UClass_AReflectTestActor_Statics::Class_MetaDataParams), Z_Construct_UClass_AReflectTestActor_Statics::Class_MetaDataParams) }; UClass* Z_Construct_UClass_AReflectTestActor() { if (!Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton) { UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton, Z_Construct_UClass_AReflectTestActor_Statics::ClassParams); } return Z_Registration_Info_UClass_AReflectTestActor.OuterSingleton; }struct Z_CompiledInDeferFile_FID_Unreal_Projects_ActionRPG_Source_ActionRPG_ReflectTestActor_h_Statics { //Z_Construct_UClass_AReflectTestActor就是OuterRegister static constexpr FClassRegisterCompiledInInfo ClassInfo[] = { { Z_Construct_UClass_AReflectTestActor, AReflectTestActor::StaticClass, TEXT("AReflectTestActor"), &Z_Registration_Info_UClass_AReflectTestActor, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AReflectTestActor), 2785921082U) }, }; };Function相关:
// Begin Class AReflectTestActor Function AddSpeed struct Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics { struct ReflectTestActor_eventAddSpeed_Parms { float InSpeed; }; #if WITH_METADATA static constexpr UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[] = { { "ModuleRelativePath", "ReflectTestActor.h" }, }; #endif // WITH_METADATA static const UECodeGen_Private::FFloatPropertyParams NewProp_InSpeed; static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[]; static const UECodeGen_Private::FFunctionParams FuncParams; }; const UECodeGen_Private::FFloatPropertyParams Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::NewProp_InSpeed = { "InSpeed", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(ReflectTestActor_eventAddSpeed_Parms, InSpeed), METADATA_PARAMS(0, nullptr) }; const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::PropPointers[] = { (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::NewProp_InSpeed, }; static_assert(UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::PropPointers) < 2048); const UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AReflectTestActor, nullptr, "AddSpeed", nullptr, nullptr, Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::PropPointers), sizeof(Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::ReflectTestActor_eventAddSpeed_Parms), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x00020401, 0, 0, METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::Function_MetaDataParams), Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::Function_MetaDataParams) }; static_assert(sizeof(Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::ReflectTestActor_eventAddSpeed_Parms) < MAX_uint16); UFunction* Z_Construct_UFunction_AReflectTestActor_AddSpeed() { static UFunction* ReturnFunction = nullptr; if (!ReturnFunction) { UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AReflectTestActor_AddSpeed_Statics::FuncParams); } return ReturnFunction; } DEFINE_FUNCTION(AReflectTestActor::execAddSpeed) { P_GET_PROPERTY(FFloatProperty,Z_Param_InSpeed); P_FINISH; P_NATIVE_BEGIN; P_THIS->AddSpeed(Z_Param_InSpeed); P_NATIVE_END; } // End Class AReflectTestActor Function AddSpeed // Begin Class AReflectTestActor Function RPC_Test struct ReflectTestActor_eventRPC_Test_Parms { float InDamage; }; static FName NAME_AReflectTestActor_RPC_Test = FName(TEXT("RPC_Test")); void AReflectTestActor::RPC_Test(float InDamage) { ReflectTestActor_eventRPC_Test_Parms Parms; Parms.InDamage=InDamage; ProcessEvent(FindFunctionChecked(NAME_AReflectTestActor_RPC_Test),&Parms); } struct Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics { #if WITH_METADATA static constexpr UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[] = { { "ModuleRelativePath", "ReflectTestActor.h" }, }; #endif // WITH_METADATA static const UECodeGen_Private::FFloatPropertyParams NewProp_InDamage; static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[]; static const UECodeGen_Private::FFunctionParams FuncParams; }; const UECodeGen_Private::FFloatPropertyParams Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::NewProp_InDamage = { "InDamage", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(ReflectTestActor_eventRPC_Test_Parms, InDamage), METADATA_PARAMS(0, nullptr) }; const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::PropPointers[] = { (const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::NewProp_InDamage, }; static_assert(UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::PropPointers) < 2048); const UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AReflectTestActor, nullptr, "RPC_Test", nullptr, nullptr, Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::PropPointers), sizeof(ReflectTestActor_eventRPC_Test_Parms), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x00220CC0, 0, 0, METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::Function_MetaDataParams), Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::Function_MetaDataParams) }; static_assert(sizeof(ReflectTestActor_eventRPC_Test_Parms) < MAX_uint16); UFunction* Z_Construct_UFunction_AReflectTestActor_RPC_Test() { static UFunction* ReturnFunction = nullptr; if (!ReturnFunction) { UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AReflectTestActor_RPC_Test_Statics::FuncParams); } return ReturnFunction; } DEFINE_FUNCTION(AReflectTestActor::execRPC_Test) { P_GET_PROPERTY(FFloatProperty,Z_Param_InDamage); P_FINISH; P_NATIVE_BEGIN; P_THIS->RPC_Test_Implementation(Z_Param_InDamage); P_NATIVE_END; } // End Class AReflectTestActor Function RPC_Test下面给出重要结构体的定义:
struct FGenericPropertyParams // : FPropertyParamsBaseWithOffset { const char* NameUTF8; const char* RepNotifyFuncUTF8; EPropertyFlags PropertyFlags; EPropertyGenFlags Flags; EObjectFlags ObjectFlags; SetterFuncPtr SetterFunc; GetterFuncPtr GetterFunc; uint16 ArrayDim; uint16 Offset; #if WITH_METADATA uint16 NumMetaData; const FMetaDataPairParam* MetaDataArray; #endif }; // This is not a base class but is just a common initial sequence of all of the F*PropertyParams types below. // We don't want to use actual inheritance because we want to construct aggregated compile-time tables of these things. struct FPropertyParamsBase { const char* NameUTF8; const char* RepNotifyFuncUTF8; EPropertyFlags PropertyFlags; EPropertyGenFlags Flags; EObjectFlags ObjectFlags; SetterFuncPtr SetterFunc; GetterFuncPtr GetterFunc; uint16 ArrayDim; }; struct FPropertyParamsBaseWithoutOffset // : FPropertyParamsBase { const char* NameUTF8; const char* RepNotifyFuncUTF8; EPropertyFlags PropertyFlags; EPropertyGenFlags Flags; EObjectFlags ObjectFlags; SetterFuncPtr SetterFunc; GetterFuncPtr GetterFunc; uint16 ArrayDim; };struct FFunctionParams { UObject* (*OuterFunc)(); UFunction* (*SuperFunc)(); const char* NameUTF8; const char* OwningClassName; const char* DelegateName; const FPropertyParamsBase* const* PropertyArray; uint16 NumProperties; uint16 StructureSize; EObjectFlags ObjectFlags; EFunctionFlags FunctionFlags; uint16 RPCId; uint16 RPCResponseId; #if WITH_METADATA uint16 NumMetaData; const FMetaDataPairParam* MetaDataArray; #endif }; struct FFunctionParams { UObject* (*OuterFunc)(); // 拥有该函数的对象的ConstructXXXX函数,如果不存在这个对象就原地构造。 UFunction* (*SuperFunc)(); // 不必多言,父函数。 const char* NameUTF8; // 函数名。 const char* OwningClassName; // 拥有该函数的函数名? const char* DelegateName; // 如果这是个委托函数,就有委托名。 const FPropertyParamsBase* const* PropertyArray; // 字面意思参数数组。 uint16 NumProperties; // 数量。 uint16 StructureSize; // 结构体大小,用于序列化。 EObjectFlags ObjectFlags; // 比如是否是抽象类的这些的元数据。 EFunctionFlags FunctionFlags; // 函数特性,是否纯虚这些。 uint16 RPCId; // 那几个RPC函数的标志。 uint16 RPCResponseId; // 识别网络请求响应,比如URL这些啥的,我也不甚了解。 };
-
重点:StaticLink( ):这里做的事情就是对前文放入ChildProperties的每个Property进行Link,其实就是设定他们大小、Flag,到下面再分门别类,比如包含对象引用的ObjectPtr..、对象析构后需要再析构的、需要内部再次初始化的属性,便于后面日后的序列化、GC、初始化的时候使用
void UStruct::Link(FArchive& Ar, bool bRelinkExistingProperties) { if (bRelinkExistingProperties) { //为Flase不会被调用,这里只会在Relink的时候调用 } else { for (FField* Field = ChildProperties; (Field != NULL) && (Field->GetOwner<UObject>() == this); Field = Field->Next) { if (FProperty* Property = CastField<FProperty>(Field)) { Property->LinkWithoutChangingOffset(Ar);//这里会调用内部的LinkInternal虚函数,所以每个不同的Property都会调用对应的虚函数 /* 下面给出例子: TProperty的LinkInternal,什么Int、FLoat都继承的这个,且都没重写,所以最后都调用的这个 virtual void LinkInternal(FArchive& Ar) override { SetElementSize(); this->PropertyFlags |= TTypeFundamentals::GetComputedFlagsPropertyFlags(); } */ } } } // Link the references, structs, and arrays for optimized cleanup. // Note: Could optimize further by adding FProperty::NeedsDynamicRefCleanup, excluding things like arrays of ints. FProperty** PropertyLinkPtr = &PropertyLink; FProperty** DestructorLinkPtr = &DestructorLink; FProperty** RefLinkPtr = (FProperty**)&RefLink; FProperty** PostConstructLinkPtr = &PostConstructLink; TArray<const FStructProperty*> EncounteredStructProps; for (TFieldIterator<FProperty> It(this); It; ++It) { FProperty* Property = *It; // Ref link contains any properties which contain object references including types with user-defined serializers which don't explicitly specify whether they // contain object references if (Property->ContainsObjectReference(EncounteredStructProps, EPropertyObjectReferenceType::Any)) { *RefLinkPtr = Property;//包含对象引用的属性 RefLinkPtr = &(*RefLinkPtr)->NextRef; } const UClass* OwnerClass = Property->GetOwnerClass(); bool bOwnedByNativeClass = OwnerClass && OwnerClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic); if (!Property->HasAnyPropertyFlags(CPF_IsPlainOldData | CPF_NoDestructor) && !bOwnedByNativeClass) // these would be covered by the native destructor { // things in a struct that need a destructor will still be in here, even though in many cases they will also be destroyed by a native destructor on the whole struct *DestructorLinkPtr = Property;//在对象析构的时候需要额外析构的属性 DestructorLinkPtr = &(*DestructorLinkPtr)->DestructorLinkNext; } // Link references to properties that require their values to be initialized and/or copied from CDO post-construction. Note that this includes all non-native-class-owned properties. if (OwnerClass && (!bOwnedByNativeClass || (Property->HasAnyPropertyFlags(CPF_Config) && !OwnerClass->HasAnyClassFlags(CLASS_PerObjectConfig)))) { *PostConstructLinkPtr = Property;//需要被初始化或者从CDO Post Construction复制来的属性,比如TArray里面的Property PostConstructLinkPtr = &(*PostConstructLinkPtr)->PostConstructLinkNext; } #if WITH_EDITORONLY_DATA // Set the bHasAssetRegistrySearchableProperties flag. // Note that we're also iterating over super class properties here so this flag is being automatically inherited bHasAssetRegistrySearchableProperties |= Property->HasAnyPropertyFlags(CPF_AssetRegistrySearchable); #endif *PropertyLinkPtr = Property;//所有Property都要链接到这里 PropertyLinkPtr = &(*PropertyLinkPtr)->PropertyLinkNext; } //链表尾部设为Nullptr *PropertyLinkPtr = nullptr; *DestructorLinkPtr = nullptr; *RefLinkPtr = nullptr; *PostConstructLinkPtr = nullptr; //后面是GC相关的 { // Now collect all references from FProperties to UObjects and store them in GC-exposed array for fast access CollectPropertyReferencedObjects(MutableView(ScriptAndPropertyObjectReferences)); #if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING // The old (non-EDL) FLinkerLoad code paths create placeholder objects // for classes and functions. We have to babysit these, just as we do // for bytecode references (reusing the AddReferencingScriptExpr fn). // Long term we should not use placeholder objects like this: for(int32 ReferenceIndex = ScriptAndPropertyObjectReferences.Num() - 1; ReferenceIndex >= 0; --ReferenceIndex) { if (ScriptAndPropertyObjectReferences[ReferenceIndex]) { if (ULinkerPlaceholderClass* PlaceholderObj = Cast<ULinkerPlaceholderClass>(ScriptAndPropertyObjectReferences[ReferenceIndex])) { // let the placeholder track the reference to it: PlaceholderObj->AddReferencingScriptExpr(reinterpret_cast<UClass**>(&ScriptAndPropertyObjectReferences[ReferenceIndex])); } // I don't currently see how placeholder functions could be present in this list, but that's // a dangerous assumption. ensure(!(ScriptAndPropertyObjectReferences[ReferenceIndex]->IsA<ULinkerPlaceholderFunction>())); } else { // It's possible that in the process of recompilation one of the refernces got GC'd leaving a null ptr in the array ScriptAndPropertyObjectReferences.RemoveAt(ReferenceIndex); } } #endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING } #if WITH_EDITORONLY_DATA // Discard old wrapper objects used by property grids for (UPropertyWrapper* Wrapper : PropertyWrappers) { Wrapper->Rename(nullptr, GetTransientPackage(), REN_ForceNoResetLoaders | REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional); Wrapper->RemoveFromRoot(); } PropertyWrappers.Empty(); #endif }virtual void LinkInternal(FArchive& Ar) override { SetElementSize(); this->PropertyFlags |= TTypeFundamentals::GetComputedFlagsPropertyFlags(); } //这里获取其类型大小 FORCEINLINE void SetElementSize() { this->ElementSize = TTypeFundamentals::CPPSize; } /** Type of the CPP property **/ typedef InTCppType TCppType; enum { CPPSize = sizeof(TCppType), CPPAlignment = alignof(TCppType) }; //这一步设置属性的PropertyFlags,用TypeTriats在计算其属性,比如是不是POD、是不是Tirvall、是不是0构造、能否哈希,然后 /** Get the property flags corresponding to this C++ type, from the C++ type traits system */ static FORCEINLINE EPropertyFlags GetComputedFlagsPropertyFlags() { return (TIsPODType<TCppType>::Value ? CPF_IsPlainOldData : CPF_None) | (TIsTriviallyDestructible<TCppType>::Value ? CPF_NoDestructor : CPF_None) | (TIsZeroConstructType<TCppType>::Value ? CPF_ZeroConstructor : CPF_None) | (TModels_V<CGetTypeHashable, TCppType> ? CPF_HasGetValueTypeHash : CPF_None); }至此,StaticClass构造完成
GetDefaultObject:梦开始的地方,创建CDO
/**
* Get the default object from the class
* @param bCreateIfNeeded if true (default) then the CDO is created if it is null
* @return the CDO for this class
*/
UObject* GetDefaultObject(bool bCreateIfNeeded = true) const
{
if (ClassDefaultObject == nullptr && bCreateIfNeeded)
{
InternalCreateDefaultObjectWrapper();
}
return ClassDefaultObject;
}
void UClass::InternalCreateDefaultObjectWrapper() const
{
UE_TRACK_REFERENCING_PACKAGE_SCOPED(this, PackageAccessTrackingOps::NAME_CreateDefaultObject);
const_cast<UClass*>(this)->CreateDefaultObject();
}
经过上面两层的调用,来到真正创建CDO的地方:
UObject* UClass::CreateDefaultObject()
{
if ( ClassDefaultObject == NULL )//第一次构造的时候进来,后面就直接返回了
{
ensureMsgf(!bLayoutChanging, TEXT("Class named %s creating its CDO while changing its layout"), *GetName());
UClass* ParentClass = GetSuperClass();//
UObject* ParentDefaultObject = NULL;
if ( ParentClass != NULL )//先初始化Parent的CDO
{
UObjectForceRegistration(ParentClass);//之前注册过的,现在不需要
ParentDefaultObject = ParentClass->GetDefaultObject(); // Force the default object to be constructed if it isn't already
check(GConfig);
if (GEventDrivenLoaderEnabled && EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME)
{
check(ParentDefaultObject && !ParentDefaultObject->HasAnyFlags(RF_NeedLoad));
}
}
if ( (ParentDefaultObject != NULL) || (this == UObject::StaticClass()) )
{
// If this is a class that can be regenerated, it is potentially not completely loaded. Preload and Link here to ensure we properly zero memory and read in properties for the CDO
//蓝图调用的部分
if( HasAnyClassFlags(CLASS_CompiledFromBlueprint) && (PropertyLink == NULL) && !GIsDuplicatingClassForReinstancing)
{
auto ClassLinker = GetLinker();
if (ClassLinker)
{
if (!GEventDrivenLoaderEnabled)
{
UField* FieldIt = Children;
while (FieldIt && (FieldIt->GetOuter() == this))
{
// If we've had cyclic dependencies between classes here, we might need to preload to ensure that we load the rest of the property chain
if (FieldIt->HasAnyFlags(RF_NeedLoad))
{
ClassLinker->Preload(FieldIt);
}
FieldIt = FieldIt->Next;
}
}
StaticLink(true);
}
}
// in the case of cyclic dependencies, the above Preload() calls could end up
// invoking this method themselves... that means that once we're done with
// all the Preload() calls we have to make sure ClassDefaultObject is still
// NULL (so we don't invalidate one that has already been setup)
if (ClassDefaultObject == NULL)
{
// RF_ArchetypeObject flag is often redundant to RF_ClassDefaultObject, but we need to tag
// the CDO as RF_ArchetypeObject in order to propagate that flag to any default sub objects.
//这里只是申请了内存,还未构造,并且标记RF_Public|RF_ClassDefaultObject|RF_ArchetypeObject这三个Flag
ClassDefaultObject = StaticAllocateObject(this, GetOuter(), NAME_None, EObjectFlags(RF_Public|RF_ClassDefaultObject|RF_ArchetypeObject));
check(ClassDefaultObject);
//下面是稀疏委托相关:
// Register the offsets of any sparse delegates this class introduces with the sparse delegate storage
for (TFieldIterator<FMulticastSparseDelegateProperty> SparseDelegateIt(this, EFieldIteratorFlags::ExcludeSuper, EFieldIteratorFlags::ExcludeDeprecated); SparseDelegateIt; ++SparseDelegateIt)
{
const FSparseDelegate& SparseDelegate = SparseDelegateIt->GetPropertyValue_InContainer(ClassDefaultObject);
USparseDelegateFunction* SparseDelegateFunction = CastChecked<USparseDelegateFunction>(SparseDelegateIt->SignatureFunction);
FSparseDelegateStorage::RegisterDelegateOffset(ClassDefaultObject, SparseDelegateFunction->DelegateName, (size_t)&SparseDelegate - (size_t)ClassDefaultObject.Get());
}
EObjectInitializerOptions InitOptions = EObjectInitializerOptions::None;
if (!HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic))
{
// Blueprint CDOs have their properties always initialized.
InitOptions |= EObjectInitializerOptions::InitializeProperties;
}
//真正调用构造函数构造CDO,这里的构造函数就是我们在StaticClass中构造UClass中传入的, ClassConstructor ( InClassConstructor )
// static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }
(*ClassConstructor)(FObjectInitializer(ClassDefaultObject, ParentDefaultObject, InitOptions));
//这里构造完广播一下
if (GetOutermost()->HasAnyPackageFlags(PKG_CompiledIn) && !GetOutermost()->HasAnyPackageFlags(PKG_RuntimeGenerated))
{
TCHAR PackageName[FName::StringBufferSize];
TCHAR CDOName[FName::StringBufferSize];
GetOutermost()->GetFName().ToString(PackageName);
GetDefaultObjectName().ToString(CDOName);
NotifyRegistrationEvent(PackageName, CDOName, ENotifyRegistrationType::NRT_ClassCDO, ENotifyRegistrationPhase::NRP_Finished, nullptr, false, ClassDefaultObject);
}
ClassDefaultObject->PostCDOContruct();//可以用户重写的地方,默认为空
}
}
}
return ClassDefaultObject;
}
至此,CDO构造完成
总结:
- CLass Default Object(CDO)的构造需要一个类的构造函数,但是C++的构造函数不能是静态函数,所以我们使用反射生成的静态函数__DefaultConstructor来包装我们的构造函数,并且依靠UClass这个反射类的构造将__DefaultConstructor传入到ClassConstructor中,所以理论上来说,我们在UClass初步构造(UClassRegisterAllCompiledInClasses)这个时候就能构造我们的CDO
- 但是在UObjectLoadAllCompiledInDefaultProperties后,UClass构造完毕,这里是收集他们的地方,正好适合做一些统一的工作,CDO的构造也就被放到了这里(猜的)
暂时不知道分类在哪的部分:
ClassWithin是如何做到限制其只能被声明在该Class的?
StaicAllocateObject在CDO构造第一步分配内存中被使用来分配内存,这里有一个check(InOuter->IsA(InClass->ClassWithin))
这里大概能做到限制

ref:https://zhuanlan.zhihu.com/p/672911428
Metadata
metadata从哪里来的:除了我们自己在属性的前加上metadata specifier之外,还有些自动生成的metadata,这些来源暂时不知道

反射生成的metadata信息如何收集:在前文有详细解释反射生成的信息如何收集的
接着就是如何收集这些metadata:前文略过了着部分内容,这里说个大概,metadata是存储在UPackage中的,在构造UClass的最后调用了AddMetaData,因为前面其UPackage已经创建了好了,这里就往里面填充metadata就可以了
class COREUOBJECT_API UMetaData : public UObject
{
public:
TMap< FWeakObjectPtr, TMap<FName, FString> > ObjectMetaDataMap;//对象关联的键值对
TMap< FName, FString > RootMetaDataMap;//包本身的键值对
};
const FString& UField::GetMetaData(const FName& Key) const
{
UPackage* Package = GetOutermost();
UMetaData* MetaData = Package->GetMetaData();
const FString& MetaDataString = MetaData->GetValue(this, Key);
return MetaDataString;
}


下面是MetaData的序列化的一个例子,和分析Object初始化流程的用到的例子一样,都是S_Actor这个SpriteTexture的反序列化
我们可以看到MetaData就是跟着Package存储的,这样虽然使用起来麻烦,但是肯定是比存储在UObject上好的,因为他是非Game Runtime的结构,只用于Editor
UnrealEditor-CoreUObject.dll!UStruct::SerializeVersionedTaggedProperties(FStructuredArchiveSlot Slot, unsigned char * Data, UStruct * DefaultsStruct, unsigned char * Defaults, const UObject * BreakRecursionIfFullyLoad) 行 1440 C++
UnrealEditor-CoreUObject.dll!UStruct::SerializeTaggedProperties(FStructuredArchiveSlot Slot, unsigned char * Data, UStruct * DefaultsStruct, unsigned char * Defaults, const UObject * BreakRecursionIfFullyLoad) 行 1334 C++
UnrealEditor-CoreUObject.dll!UObject::SerializeScriptProperties(FStructuredArchiveSlot Slot) 行 1880 C++
UnrealEditor-CoreUObject.dll!UObject::Serialize(FStructuredArchiveRecord Record) 行 1661 C++
UnrealEditor-CoreUObject.dll!UMetaData::Serialize(FStructuredArchiveRecord Record) 行 124 C++
UnrealEditor-CoreUObject.dll!UMetaData::Serialize(FArchive & Ar) 行 116 C++
UnrealEditor-CoreUObject.dll!FLinkerLoad::Preload(UObject * Object) 行 4761 C++
> UnrealEditor-CoreUObject.dll!EndLoad(FUObjectSerializeContext * LoadContext, TArray<UPackage *,TSizedDefaultAllocator<32>> * OutLoadedPackages) 行 2199 C++
UnrealEditor-CoreUObject.dll!LoadPackageInternal::__l110::<lambda_2>::operator()() 行 1810 C++
UnrealEditor-CoreUObject.dll!LoadPackageInternal(UPackage * InOuter, const FPackagePath & PackagePath, unsigned int LoadFlags, FLinkerLoad * ImportLinker, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext, const FPackagePath * DiffPackagePath) 行 1912 C++
UnrealEditor-CoreUObject.dll!LoadPackage(UPackage * InOuter, const FPackagePath & PackagePath, unsigned int LoadFlags, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext, const FPackagePath * DiffPackagePath) 行 2068 C++
UnrealEditor-CoreUObject.dll!LoadPackage(UPackage * InOuter, const wchar_t * InLongPackageNameOrFilename, unsigned int LoadFlags, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext) 行 2044 C++
UnrealEditor-CoreUObject.dll!ResolveName(UObject * & InPackage, FString & InOutName, bool Create, bool Throw, unsigned int LoadFlags, const FLinkerInstancingContext * InstancingContext) 行 1246 C++
UnrealEditor-CoreUObject.dll!StaticLoadObjectInternal(UClass * ObjectClass, UObject * InOuter, const wchar_t * InName, const wchar_t * Filename, unsigned int LoadFlags, UPackageMap * Sandbox, bool bAllowObjectReconciliation, const FLinkerInstancingContext * InstancingContext) 行 1356 C++
UnrealEditor-CoreUObject.dll!StaticLoadObject(UClass * ObjectClass, UObject * InOuter, const wchar_t * InName, const wchar_t * Filename, unsigned int LoadFlags, UPackageMap * Sandbox, bool bAllowObjectReconciliation, const FLinkerInstancingContext * InstancingContext) 行 1430 C++
[内联框架] UnrealEditor-Engine.dll!LoadObject(UObject *) 行 1959 C++
UnrealEditor-Engine.dll!ConstructorHelpersInternal::FindOrLoadObject<UTexture2D>(FString & PathName, unsigned int LoadFlags) 行 39 C++
[内联框架] UnrealEditor-Engine.dll!ConstructorHelpers::FObjectFinder<UTexture2D>::{ctor}(const wchar_t *) 行 90 C++
[内联框架] UnrealEditor-Engine.dll!UBillboardComponent::{ctor}::__l2::FConstructorStatics::{ctor}() 行 266 C++
UnrealEditor-Engine.dll!UBillboardComponent::UBillboardComponent(const FObjectInitializer & ObjectInitializer) 行 273 C++
UnrealEditor-CoreUObject.dll!UClass::CreateDefaultObject() 行 4586 C++
UnrealEditor-CoreUObject.dll!UClass::InternalCreateDefaultObjectWrapper() 行 5190 C++
[内联框架] UnrealEditor-CoreUObject.dll!UClass::GetDefaultObject(bool) 行 3239 C++
UnrealEditor-CoreUObject.dll!FObjectInitializer::CreateDefaultSubobject(UObject * Outer, FName SubobjectFName, const UClass * ReturnType, const UClass * ClassToCreateByDefault, bool bIsRequired, bool bIsTransient) 行 5427 C++
UnrealEditor-CoreUObject.dll!FObjectInitializer::CreateEditorOnlyDefaultSubobject(UObject * Outer, FName SubobjectName, const UClass * ReturnType, bool bTransient) 行 5495 C++
UnrealEditor-CoreUObject.dll!UObject::CreateEditorOnlyDefaultSubobjectImpl(FName SubobjectName, UClass * ReturnType, bool bTransient) 行 171 C++
[内联框架] UnrealEditor-Engine.dll!UObject::CreateEditorOnlyDefaultSubobject(FName) 行 134 C++
UnrealEditor-Engine.dll!AReflectionCapture::AReflectionCapture(const FObjectInitializer & ObjectInitializer) 行 154 C++
UnrealEditor-CoreUObject.dll!UClass::CreateDefaultObject() 行 4586 C++
UnrealEditor-CoreUObject.dll!UClass::InternalCreateDefaultObjectWrapper() 行 5190 C++
[内联框架] UnrealEditor-CoreUObject.dll!UClass::GetDefaultObject(bool) 行 3239 C++
UnrealEditor-CoreUObject.dll!UClass::CreateDefaultObject() 行 4529 C++
UnrealEditor-CoreUObject.dll!UClass::InternalCreateDefaultObjectWrapper() 行 5190 C++
[内联框架] UnrealEditor-CoreUObject.dll!UClass::GetDefaultObject(bool) 行 3239 C++
UnrealEditor-CoreUObject.dll!UObjectLoadAllCompiledInDefaultProperties(TArray<UClass *,TSizedDefaultAllocator<32>> & OutAllNewClasses) 行 800 C++
UnrealEditor-CoreUObject.dll!ProcessNewlyLoadedUObjects(FName Package, bool bCanProcessNewlyLoadedObjects) 行 894 C++
UnrealEditor.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine) 行 3803 C++
[内联框架] UnrealEditor.exe!FEngineLoop::PreInit(const wchar_t *) 行 4483 C++
[内联框架] UnrealEditor.exe!EnginePreInit(const wchar_t *) 行 41 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) 行 136 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) 行 247 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) 行 298 C++
[内联框架] UnrealEditor.exe!invoke_main() 行 102 C++
UnrealEditor.exe!__scrt_common_main_seh() 行 288 C++
kernel32.dll!00007ff8aa3d7374() 未知
ntdll.dll!00007ff8abedcc91() 未知
void UMetaData::Serialize(FStructuredArchive::FRecord Record)
{
FArchive& UnderlyingArchive = Record.GetUnderlyingArchive();
Super::Serialize(Record);
UnderlyingArchive.UsingCustomVersion(FEditorObjectVersion::GUID);
if( UnderlyingArchive.IsSaving() )
{
// Remove entries belonging to destructed objects
for (TMap< FWeakObjectPtr, TMap<FName, FString> >::TIterator It(ObjectMetaDataMap); It; ++It)
{
if (!It.Key().IsValid())
{
It.RemoveCurrent();
}
}
}
if (!UnderlyingArchive.IsLoading())
{
Record << SA_VALUE(TEXT("ObjectMetaDataMap"), ObjectMetaDataMap);
Record << SA_VALUE(TEXT("RootMetaDataMap"), RootMetaDataMap);
}
else
{
{
TMap< FWeakObjectPtr, TMap<FName, FString> > TempMap;
Record << SA_VALUE(TEXT("ObjectMetaDataMap"), TempMap);
const bool bLoadFromLinker = (NULL != UnderlyingArchive.GetLinker());
if (bLoadFromLinker && HasAnyFlags(RF_LoadCompleted))
{
UE_LOG(LogMetaData, Verbose, TEXT("Metadata was already loaded by linker. %s"), *GetFullName());
}
else
{
if (bLoadFromLinker && ObjectMetaDataMap.Num())
{
UE_LOG(LogMetaData, Verbose, TEXT("Metadata: Some values, filled while serialization, may be lost. %s"), *GetFullName());
}
Swap(ObjectMetaDataMap, TempMap);
}
}
if (UnderlyingArchive.CustomVer(FEditorObjectVersion::GUID) >= FEditorObjectVersion::RootMetaDataSupport)
{
TMap<FName, FString> TempMap;
Record << SA_VALUE(TEXT("RootMetaDataMap"), TempMap);
const bool bLoadFromLinker = (NULL != UnderlyingArchive.GetLinker());
if (bLoadFromLinker && HasAnyFlags(RF_LoadCompleted))
{
UE_LOG(LogMetaData, Verbose, TEXT("Root metadata was already loaded by linker. %s"), *GetFullName());
}
else
{
if (bLoadFromLinker && RootMetaDataMap.Num())
{
UE_LOG(LogMetaData, Verbose, TEXT("Metadata: Some root values, filled while serialization, may be lost. %s"), *GetFullName());
}
Swap(RootMetaDataMap, TempMap);
}
}
// Run redirects on loaded keys
InitializeRedirectMap();
for (TMap< FWeakObjectPtr, TMap<FName, FString> >::TIterator ObjectIt(ObjectMetaDataMap); ObjectIt; ++ObjectIt)
{
TMap<FName, FString>& CurrentMap = ObjectIt.Value();
for (TMap<FName, FString>::TIterator PairIt(CurrentMap); PairIt; ++PairIt)
{
const FName OldKey = PairIt.Key();
const FName NewKey = KeyRedirectMap.FindRef(OldKey);
if (NewKey != NAME_None)
{
const FString Value = PairIt.Value();
PairIt.RemoveCurrent();
CurrentMap.Add(NewKey, Value);
UE_LOG(LogMetaData, Verbose, TEXT("Remapping old metadata key '%s' to new key '%s' on object '%s'."), *OldKey.ToString(), *NewKey.ToString(), *ObjectIt.Key().Get()->GetPathName());
}
}
}
for (TMap<FName, FString>::TIterator PairIt(RootMetaDataMap); PairIt; ++PairIt)
{
const FName OldKey = PairIt.Key();
const FName NewKey = KeyRedirectMap.FindRef(OldKey);
if (NewKey != NAME_None)
{
const FString Value = PairIt.Value();
PairIt.RemoveCurrent();
RootMetaDataMap.Add(NewKey, Value);
UE_LOG(LogMetaData, Verbose, TEXT("Remapping old metadata key '%s' to new key '%s' on root."), *OldKey.ToString(), *NewKey.ToString());
}
}
}
#if 0 && WITH_EDITOR
FMetaDataUtilities::DumpMetaData(this);
#endif
}
CoreUObject系统的初始化
上面的整个分析流程,都是其他模块(DLL)中的反射信息(UClass、UScriptStruct)的构建流程,但是CoreUObject这个模块,作为一切的基础,比如说GUObjectAllocator,这个是UObject分配内存的来源,所有UObject构造的过程中,一定会用到GUObjectAllocator分配内存,甚至包括UClass。
我们可以看出来,在引擎初始化的阶段的PreInitPreStartupScreen,我们会先加载CoreUObjectModule,在这个module的StartupModule中,会调用一次UClassRegisterAllCompiledInClasses,接着他会将initUObject加入到Appinit中,接着在CoreUObjectModule后面就会调用这个initUObject,initUObject里面会完成对GUObjectAllocator和GUObjectArray中的内存池进行初始化,接着标记UObject系统初始化完毕,然后调用UObjectProcessRegistrants
对CoreUobject这个模块初始化的过程,我们可以看到,他用的方法和其他模块的是一样的,都是UClassRegisterAllCompiledInClasses对静态收集的信息进行收集到一个地方,并完成初步的UClass的构造,然后在UObjectProcessRegistrants中完成对CLassPrivate和OuterPrivate的赋值,然后放入全局的GUObjectArray和GUObjectHashtables中。
这里的分步骤的意义在于,第一步的时候,我们还不能正确构造UObject(因为还没有调用initUObject),而UPackage是UObject,所以这些UClass的Outer都没法赋值,而且





// Note initialized.
Internal::GetUObjectSubsystemInitialised() = true;
UObjectProcessRegistrants();
Flags
还有一个东西是有点略过的,就是各种Flags的枚举。UE利用这些枚举标志来判断对象的状态和特征。 重要的有:
- EObjectFlags:对象本身的标志。
- EInternalObjectFlags:对象存储的标志,GC的时候用来检查可达性。
- EObjectMark:用来额外标记对象特征的标志,用在序列化过程中标识状态。
- EClassFlags:类的标志,定义了一个类的特征。
- EClassCastFlags:类之间的转换,可以快速的测试一个类是否可以转换成某种类型。
- EStructFlags:结构的特征标志。
- EFunctionFlags:函数的特征标志。
- EPropertyFlags:属性的特征标志。
具体的请读者们自己去查看定义了,太多了就不一一解释了。这也是一种常用的C++惯用法,枚举标志来表示叠加的特征。
作者:大钊
链接:https://zhuanlan.zhihu.com/p/60291730
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
实验性功能:
SparseClassData 稀疏类数据: 是一个UClass的一个实验性新功能,如果一些属性不会在实例化后更改,比如他是一个ReadOnly的,那么就可以放在这里,类似一个运行时的常量,可以做类似字符串的短字符串优化(所有类实例共用一个这些数据的实例),这样就可以减少内存的使用
References:InsideUE4、UE4中的反射之一:编译阶段、UE5 UObject类型系统和反射
、Yuerer的UE5反射代码生成与注册、凌泽的UOBJECT系列、李嘉图的UE5反射、UECOREDOCUMENTATION-github、




浙公网安备 33010602011771号