ue4 NewObject

10287 ワード

出典:http://www.cnblogs.com/wellbye/p/5808894.html
ue4 NewObject/StaticConstructObject_Internal/staticAllocateObject/FObjectInitializer:オブジェクトの作成と初期化
UObjectは複雑なシステムであり、以前はue 3コードを読んだときにそのタイプのシステム実装を分析したことがあり、主にUClassとの関係である.
今ue 4に移って、その1枚が実は多くないことを発見して、そこで更に重点的に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(StaticConstructObject_Internal(Class, Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph));
}


いくつかの条件の検査を除いて、以前の古い版の対応物StaticConstructObjectを直接調整しました.Internal.
ここでのいくつかのパラメータは,興味深いことに5番目のパラメータTemplateであり,それによってオブジェクトをコピーすることができる.
 
2、StaticConstructObject_Internal、この中で主に2つのことをしています.
一つはStaticAllocateObjectで空間を割り当てることで、
2つ目は、この空間でオブジェクトを初期化することです.
 
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に必要なパラメータであり、前者も各UObjectに定義されたマクロ(DECLARE_CLASS)による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】が自動的に解析されるので、ue 4はこのステップを利用して、この中で多くのことをしましたが、ほとんどのことはこのシステムの内部状態管理に関係しており、一時的に理解しにくいです.
しかし、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です.
コードには、この2つの状況に対する異なる処理ポリシーが明らかに示されています.
コピーするオブジェクトTemplate->DefaultDataが指定されている場合は、クラスのすべてのプロパティを巡回します.実際のコピーターゲットでは、どのプロパティが変更されているか(デフォルトではありません)分からないため、すべてコピーする必要があります.
CDOからコピーするだけなら、CPF_を打つなど、初期化状態が指定されている可能性のあるフィールドを処理するだけです.起動時にiniファイルから適切な構成値を抽出するConfigタグのフィールド.