ue4 NewObject

出自:http://www.cnblogs.com/wellbye/p/5808894.html

ue4 NewObject/StaticConstructObject_Internal/StaticAllocateObject/FObjectInitializer:對象創建和初始化

UObject是一套很複雜的體系,之前讀ue3代碼時曾分析過其類型系統實現,主要是與UClass間的關係

現在轉到ue4,發現那一塊其實差不多,於是再重點備忘一下UObject本身的創建和初始化過程

 

1、首先,用NewObject<>來創建一個新對象:

複製代碼
template< class T >
T* NewObject(UObject* Outer, UClass* Class, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr)
{
    if (Name == NAME_None)
    {
        FObjectInitializer::AssertIfInConstructor(Outer, TEXT("NewObject with empty name can't be used to create default subobjects (inside of UObject derived class constructor) as it produces inconsistent object names. Use ObjectInitializer.CreateDefaultSuobject<> instead."));
    }

#if DO_CHECK
    // Class was specified explicitly, so needs to be validated
    CheckIsClassChildOf_Internal(T::StaticClass(), Class);
#endif

    return static_cast<T*>(StaticConstructObject_Internal(Class, Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph));
}
複製代碼

除了一些條件檢測,直接調了以前老版的對應物StaticConstructObject_Internal。

這裏的幾個參數,有意思的是第5個參數Template,通過它可以複製對象。

 

2、StaticConstructObject_Internal,這裏面主要做兩件事:

一是用StaticAllocateObject來分配空間,

二是在此空間上初始化對象。

 

3、StaticAllocateObject:

首先是檢查要創建新對象,還是替換一個原有對象,因爲一路傳進來的有個Name參數。如果找到一個已有的同名對象,那麼會強制那個對象析構,然後重用它的空間,否則的話,就會直接分配空間了:

UObjectBase* FUObjectAllocator::AllocateUObject(int32 Size, int32 Alignment, bool bAllowPermanent)

除了這件主要的工作,還有不少其它的事,比如管理對象之間的連接、在異步線程中創建對象時的通知等等,暫時不深究了。

 

4、有了前者分配的空間,那麼就可以在此處構造對象了:

     Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject);
        (*InClass->ClassConstructor)( FObjectInitializer(Result, InTemplate, bCopyTransientsFromClassDefaults, true, InInstanceGraph) );

這裏ClassConstructor是每個UClass都有的一個函數指針類成員變量,實際上所有的UClass裏該指針都指向一個全局模板函數:

template<class T>
void InternalConstructor( const FObjectInitializer& X )
{ 
    T::__DefaultConstructor(X);
}

而每個類裏的__DefaultConstructor也是用宏統一生成的,內容也是簡單的轉發給new:

static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }

這個new的形式很不平凡,既有傳給operator new的【(EInternal*)X.GetObj()】,又有傳給該類實際構造函數的【FObjectInitializer& X】

後者正是InClass->ClassConstructor需要的實參,而前者也是通過宏(DECLARE_CLASS)定義在每個UObject裏的一個operator new:

inline void* operator new( const size_t InSize, EInternal* InMem ) \
    { \
        return (void*)InMem; \
    }

總結一下上面繞來繞去的東西就是:

StaticAllocateObject分配了內存空間Result,然後以此爲參數(當然還有其它的參數)構造了一個FObjectInitializer X,其中X.GetObj返回的就是這個內存地址Result;

接着用【 new(Result) TClass(X) 】來構造對象,指明地址在Result上,並將X做爲參數傳給其構造函數。

 

5、關於FObjectInitializer

接上步,在構造函數返回後,其臨時參數【FObjectInitializer& X】是要自動析構的,因而ue4利用此步驟,在這裏面做了很多事,大部份的事都是與該體系內部狀態管理有關的,暫時難以透徹理解。

但有一個的操作與應用相關,即屬性初始化,這是通過InitProperties來完成的:

複製代碼
void FObjectInitializer::InitProperties(UObject* Obj, UClass* DefaultsClass, UObject* DefaultData, bool bCopyTransientsFromClassDefaults)
{
  ……
    UClass* Class = Obj->GetClass();
  ……if (!bNeedInitialize && bCanUsePostConstructLink)
    {
        // This is just a fast path for the below in the common case that we are not doing a duplicate or initializing a CDO and this is all native.
        // We only do it if the DefaultData object is NOT a CDO of the object that's being initialized. CDO data is already initialized in the
        // object's constructor.
        if (DefaultData)
        {
            if (Class->GetDefaultObject(false) != DefaultData)
            {
                QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_FromTemplate);
                for (UProperty* P = Class->PropertyLink; P; P = P->PropertyLinkNext)
                {
                    P->CopyCompleteValue_InContainer(Obj, DefaultData);
                }
            }
            else
            {
                QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_ConfigEtcOnly);
                // Copy all properties that require additional initialization (e.g. CPF_Config).
                for (UProperty* P = Class->PostConstructLink; P; P = P->PostConstructLinkNext)
                {
                    P->CopyCompleteValue_InContainer(Obj, DefaultData);
                }
            }
        }
  ……
複製代碼

在這裏通過遍歷UClass上記錄的屬性元數據,可以對當前實例的每個屬性進行賦值。

有趣的是DefaultData這個參數,也就是最早的Template參數,一路輾轉到此。當然如果Template爲空的話,這裏的DefaultData就是該類的CDO了。

代碼裏明顯體現出對此兩種情況的不同處理策略:

如果是指定了要複製的對象Template->DefaultData,那麼要遍歷類上的所有的屬性,因爲對於一個實際的複製目標,你不知道它哪些屬性已經改變了(不是默認值 ),因此必須全盤複製

而如果只是從CDO複製的話,那麼只需要處理該類裏明確指定過可能有初始化狀態的字段,如打上CPF_Config標記的字段,它們在啓動時會去ini文件裏提取相應的配置值。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章