UE4对象系统_对象的创建和查找

本篇文章通过调试代码进行跟踪对象的创建和查找,使用UE4 Example FirstPersonCPP工程。
官方文档https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/index.html

NewObject

新建一个UPlayerData类,定义如下:

UCLASS()
class FIRSTPERSONCPP_API UPlayerData : public UObject
{
    GENERATED_BODY()
    
    
public:
    UPlayerData();


    FString     PlayerName;
    uint32_t    BulletNum;
    
};

// Fill out your copyright notice in the Description page of Project Settings.

#include "PlayerData.h"


UPlayerData::UPlayerData()
{
    PlayerName = TEXT("Tom");
    BulletNum = 100;
}

在void AFirstPersonCPPCharacter::OnFire()中加入测试代码

{
    UPlayerData *PlayerData = NewObject(this, FName(TEXT("Jackson")));
    check(PlayerData);
        ...
}

堆栈如下:

UE4对象系统_对象的创建和查找_第1张图片
Paste_Image.png

经过跟踪调试, 下面分析如下相关函数:

  1. NewObject
template< class T >
FUNCTION_NON_NULL_RETURN_START
    T* NewObject(UObject* Outer, FName Name, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr)
FUNCTION_NON_NULL_RETURN_END
{
    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."));
    }

    return static_cast(StaticConstructObject_Internal(T::StaticClass(), Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph));
}
  1. StaticConstructObject_Internal
UObject* StaticConstructObject_Internal
(
    UClass*         InClass,
    UObject*        InOuter                             /*=GetTransientPackage()*/,
    FName           InName                              /*=NAME_None*/,
    EObjectFlags    InFlags                             /*=0*/,
    EInternalObjectFlags InternalSetFlags /*=0*/,
    UObject*        InTemplate                          /*=NULL*/,
    bool bCopyTransientsFromClassDefaults   /*=false*/,
    FObjectInstancingGraph* InInstanceGraph             /*=NULL*/,
    bool bAssumeTemplateIsArchetype /*=false*/
)
{
    SCOPE_CYCLE_COUNTER(STAT_ConstructObject);
    UObject* Result = NULL;

#if WITH_EDITORONLY_DATA
    UE_CLOG(GIsSavingPackage && InOuter != GetTransientPackage(), LogUObjectGlobals, Fatal, TEXT("Illegal call to StaticConstructObject() while serializing object data! (Object will not be saved!)"));
#endif

    checkf(!InTemplate || InTemplate->IsA(InClass) || (InFlags & RF_ClassDefaultObject), TEXT("StaticConstructObject %s is not an instance of class %s and it is not a CDO."), *GetFullNameSafe(InTemplate), *GetFullNameSafe(InClass)); // template must be an instance of the class we are creating, except CDOs

    // Subobjects are always created in the constructor, no need to re-create them unless their archetype != CDO or they're blueprint generated.
    // If the existing subobject is to be re-used it can't have BeginDestroy called on it so we need to pass this information to StaticAllocateObject.  
    const bool bIsNativeClass = InClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic);
    const bool bIsNativeFromCDO = bIsNativeClass &&
        (
            !InTemplate || 
            (InName != NAME_None && (bAssumeTemplateIsArchetype || InTemplate == UObject::GetArchetypeFromRequiredInfo(InClass, InOuter, InName, InFlags)))
        );
#if WITH_HOT_RELOAD
    // Do not recycle subobjects when performing hot-reload as they may contain old property values.
    const bool bCanRecycleSubobjects = bIsNativeFromCDO && !GIsHotReload;
#else
    const bool bCanRecycleSubobjects = bIsNativeFromCDO;
#endif
    bool bRecycledSubobject = false;    
    /*
     * Create a new instance of an object or replace an existing object.  If both an Outer and Name are specified, and there is an object already in memory with the same Class, Outer, and Name, the
     * existing object will be destructed, and the new object will be created in its place.
     */
    Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject);
    check(Result != NULL);
    // Don't call the constructor on recycled subobjects, they haven't been destroyed.
    if (!bRecycledSubobject)
    {       
                // 调用构造函数
        FScopeCycleCounterUObject ConstructorScope(InClass, GET_STATID(STAT_ConstructObject));
        (*InClass->ClassConstructor)( FObjectInitializer(Result, InTemplate, bCopyTransientsFromClassDefaults, true, InInstanceGraph) );
    }
    
    if( GIsEditor && GUndo && (InFlags & RF_Transactional) && !(InFlags & RF_NeedLoad) && !InClass->IsChildOf(UField::StaticClass()) )
    {
        // Set RF_PendingKill and update the undo buffer so an undo operation will set RF_PendingKill on the newly constructed object.
        Result->MarkPendingKill();
        SaveToTransactionBuffer(Result, false);
        Result->ClearPendingKill();
    }
    return Result;
}
  1. StaticAllocateObject
    主要是分配对象内存,并且设定名字空间和注册
UObject* StaticAllocateObject
(
    UClass*         InClass,
    UObject*        InOuter,
    FName           InName,
    EObjectFlags    InFlags,
    EInternalObjectFlags InternalSetFlags,
    bool bCanRecycleSubobjects,
    bool* bOutRecycledSubobject
)
{
    SCOPE_CYCLE_COUNTER(STAT_AllocateObject);
    checkSlow(InOuter != INVALID_OBJECT); // not legal
    check(!InClass || (InClass->ClassWithin && InClass->ClassConstructor));
#if WITH_EDITOR
    if (GIsEditor)
    {
        if (StaticAllocateObjectErrorTests(InClass,InOuter,InName,InFlags))
        {
            return NULL;
        }
    }
#endif // WITH_EDITOR
    bool bCreatingCDO = (InFlags & RF_ClassDefaultObject) != 0;

    check(InClass);
    check(GIsEditor || bCreatingCDO || !InClass->HasAnyClassFlags(CLASS_Abstract)); // this is a warning in the editor, otherwise it is illegal to create an abstract class, except the CDO
    check(InOuter || (InClass == UPackage::StaticClass() && InName != NAME_None)); // only packages can not have an outer, and they must be named explicitly
    check(bCreatingCDO || !InOuter || InOuter->IsA(InClass->ClassWithin)); // 判断InOuter是否属于ClassWithin类


    if (bCreatingCDO)
    {
        check(InClass->GetClass());
        if( !GIsDuplicatingClassForReinstancing )
        {
            InName = InClass->GetDefaultObjectName();
        }
        // never call PostLoad on class default objects
        InFlags &= ~(RF_NeedPostLoad|RF_NeedPostLoadSubobjects);
    }

    UObject* Obj = NULL;
    if(InName == NAME_None)
    {
                // 分配对象名字
#if WITH_EDITOR
        if ( GOutputCookingWarnings && GetTransientPackage() != InOuter->GetOutermost() )
        {
            static const FName NAME_UniqueObjectNameForCooking(TEXT("UniqueObjectNameForCooking"));
            InName = MakeUniqueObjectName(InOuter, InClass, NAME_UniqueObjectNameForCooking);
        }
        else
#endif
        {
            InName = MakeUniqueObjectName(InOuter, InClass);
        }
    }
    else
    {
                // 判断对象是否已经存在
        // See if object already exists.
        Obj = StaticFindObjectFastInternal( /*Class=*/ NULL, InOuter, InName, true );

        // Temporary: If the object we found is of a different class, allow the object to be allocated.
        // This breaks new UObject assumptions and these need to be fixed.
        if (Obj && !Obj->GetClass()->IsChildOf(InClass))
        {
            UE_LOG(LogUObjectGlobals, Fatal,
                TEXT("Objects have the same fully qualified name but different paths.\n")
                TEXT("\tNew Object: %s %s.%s\n")
                TEXT("\tExisting Object: %s"),
                *InClass->GetName(), InOuter ? *InOuter->GetPathName() : TEXT(""), *InName.ToString(),
                *Obj->GetFullName());
        }
    }

    FLinkerLoad*    Linker                      = NULL;
    int32               LinkerIndex                 = INDEX_NONE;
    bool            bWasConstructedOnOldObject  = false;
    // True when the object to be allocated already exists and is a subobject.
    bool bSubObject = false;
    int32 TotalSize = InClass->GetPropertiesSize();
    checkSlow(TotalSize);

    if( Obj == NULL )
    {   // 没有可复用的对象,新分配内存
        int32 Alignment = FMath::Max( 4, InClass->GetMinAlignment() );
        Obj = (UObject *)GUObjectAllocator.AllocateUObject(TotalSize,Alignment,GIsInitialLoad);
    }
    else
    {
                // 复用已经存在的Object空间
        // Replace an existing object without affecting the original's address or index.
        check(!Obj->IsUnreachable());

        check(!ObjectRestoreAfterInitProps); // otherwise recursive construction
        ObjectRestoreAfterInitProps = Obj->GetRestoreForUObjectOverwrite();

        // Remember linker, flags, index, and native class info.
        Linker      = Obj->GetLinker();
        LinkerIndex = Obj->GetLinkerIndex();
        InternalSetFlags |= (Obj->GetInternalFlags() & (EInternalObjectFlags::Native | EInternalObjectFlags::RootSet));

        if ( bCreatingCDO )
        {
            check(Obj->HasAllFlags(RF_ClassDefaultObject));
            Obj->SetFlags(InFlags);
            Obj->SetInternalFlags(InternalSetFlags);
            // never call PostLoad on class default objects
            Obj->ClearFlags(RF_NeedPostLoad|RF_NeedPostLoadSubobjects);
        }
        else if(!InOuter || !InOuter->HasAnyFlags(RF_ClassDefaultObject))
        {
#if !UE_BUILD_SHIPPING
            // Handle nested DSOs
            bool bIsOwnedByCDO = false;
            UObject* Iter = InOuter;
            while (Iter)
            {
                if (Iter->HasAnyFlags(RF_ClassDefaultObject))
                {
                    bIsOwnedByCDO = true;
                    break;
                }
                Iter = Iter->GetOuter();
            }
            // Should only get in here if we're NOT creating a subobject of a CDO.  CDO subobjects may still need to be serialized off of disk after being created by the constructor
            // if really necessary there was code to allow replacement of object just needing postload, but lets not go there unless we have to
            checkf(!Obj->HasAnyFlags(RF_NeedLoad|RF_NeedPostLoad|RF_ClassDefaultObject) || bIsOwnedByCDO,
                *FText::Format(NSLOCTEXT("Core", "ReplaceNotFullyLoaded_f", "Attempting to replace an object that hasn't been fully loaded: {0} (Outer={1}, Flags={2})"),
                    FText::FromString(Obj->GetFullName()),
                    InOuter ? FText::FromString(InOuter->GetFullName()) : FText::FromString(TEXT("NULL")),
                    FText::FromString(FString::Printf(TEXT("0x%08x"), (int32)Obj->GetFlags()))).ToString());
#endif//UE_BUILD_SHIPPING
        }
        // Subobjects are always created in the constructor, no need to re-create them here unless their archetype != CDO or they're blueprint generated.   
        if (!bCreatingCDO && (!bCanRecycleSubobjects || !Obj->IsDefaultSubobject()))
        {
            // Destroy the object.
            SCOPE_CYCLE_COUNTER(STAT_DestroyObject);
            // Check that the object hasn't been destroyed yet.
            if(!Obj->HasAnyFlags(RF_FinishDestroyed))
            {
                // Get the name before we start the destroy, as destroy renames it
                FString OldName = Obj->GetFullName();

                // Begin the asynchronous object cleanup.
                Obj->ConditionalBeginDestroy();

                // Wait for the object's asynchronous cleanup to finish.
                while (!Obj->IsReadyForFinishDestroy()) 
                {
                    // If we're not in the editor, and aren't doing something specifically destructive like reconstructing blueprints, this is fatal
                    if (!GIsEditor && FApp::IsGame() && !GIsReconstructingBlueprintInstances)
                    {
                        // Switching to warning, investigate why level duplication triggers this
                        UE_LOG(LogUObjectGlobals, Warning, TEXT("Gamethread hitch waiting for resource cleanup on a UObject (%s) overwrite. Fix the higher level code so that this does not happen."), *OldName );
                    }
                    FPlatformProcess::Sleep(0);
                }
                // Finish destroying the object.
                Obj->ConditionalFinishDestroy();
            }
            Obj->~UObject();
            bWasConstructedOnOldObject  = true;
        }
        else
        {
            bSubObject = true;
        }
    }

    // If class is transient, non-archetype objects must be transient.
    bool const bCreatingArchetype = (InFlags & RF_ArchetypeObject) != 0;
    if ( !bCreatingCDO && InClass->HasAnyClassFlags(CLASS_Transient) && !bCreatingArchetype )
    {
        InFlags |= RF_Transient;
    }

    if (!bSubObject)
    {
        FMemory::Memzero((void *)Obj, TotalSize);
                // 在该Obj处调用UObjectBase构造函数
        new ((void *)Obj) UObjectBase(InClass, InFlags|RF_NeedInitialization, InternalSetFlags, InOuter, InName);
    }
    else
    {
        // Propagate flags to subobjects created in the native constructor.
        Obj->SetFlags(InFlags);
        Obj->SetInternalFlags(InternalSetFlags);
    }

    if (bWasConstructedOnOldObject)
    {
        // Reassociate the object with it's linker.
        Obj->SetLinker(Linker,LinkerIndex,false);
        if(Linker)
        {
            check(Linker->ExportMap[LinkerIndex].Object == NULL);
            Linker->ExportMap[LinkerIndex].Object = Obj;
        }
    }

    if (IsInAsyncLoadingThread())
    {
        NotifyConstructedDuringAsyncLoading(Obj, bSubObject);
    }
    else
    {
        // Sanity checks for async flags.
        // It's possible to duplicate an object on the game thread that is still being referenced 
        // by async loading code or has been created on a different thread than the main thread.
        Obj->ClearInternalFlags(EInternalObjectFlags::AsyncLoading);
        if (Obj->HasAnyInternalFlags(EInternalObjectFlags::Async) && IsInGameThread())
        {
            Obj->ClearInternalFlags(EInternalObjectFlags::Async);
        }
    }


    // Let the caller know if a subobject has just been recycled.
    if (bOutRecycledSubobject)
    {
        *bOutRecycledSubobject = bSubObject;
    }
    
    return Obj;
}
/**
 * Constructor used by StaticAllocateObject
 * @param   InClass             non NULL, this gives the class of the new object, if known at this time
 * @param   InFlags             RF_Flags to assign
 * @param   InOuter             outer for this object
 * @param   InName              name of the new object
 * @param   InObjectArchetype   archetype to assign
 */
UObjectBase::UObjectBase(UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName)
:   ObjectFlags         (InFlags)
,   InternalIndex       (INDEX_NONE)
,   ClassPrivate        (InClass)
,   OuterPrivate        (InOuter)
{
    check(ClassPrivate);
    // Add to global table.  这里登记对象
    AddObject(InName, InInternalFlags);
}

创建流程小结

  1. 快速查找是否已经存在同名对象;
  2. 如果存在则析构掉,并利用该内存块m;
  3. 如果不存在同名对象,则根据Class反射信息 分配内存块m;
  4. 在内存块m上调用构造函数ObjectBase。
  5. 最后在内存块m上调用我们期望的类的构造函数
    其中UObjectBase(UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName)函数完成了对象登记。下面通过分析该函数来了解对象管理。

全局对象表

/**
 * Add a newly created object to the name hash tables and the object array
 *
 * @param Name name to assign to this uobject
 */
void UObjectBase::AddObject(FName InName, EInternalObjectFlags InSetInternalFlags)
{
    NamePrivate = InName;
    EInternalObjectFlags InternalFlagsToSet = InSetInternalFlags;
    if (!IsInGameThread())
    {
        InternalFlagsToSet |= EInternalObjectFlags::Async;  // 在其它线程创建标志, 主要用在异步加载
    }
    if (ObjectFlags & RF_MarkAsRootSet)
    {       
        InternalFlagsToSet |= EInternalObjectFlags::RootSet; // 不可GC
        ObjectFlags &= ~RF_MarkAsRootSet;
    }
    if (ObjectFlags & RF_MarkAsNative)
    {
        InternalFlagsToSet |= EInternalObjectFlags::Native; // C++代码中创建的对象
        ObjectFlags &= ~RF_MarkAsNative;
    }
    AllocateUObjectIndexForCurrentThread(this);  // 在GlobalArray中分配对象信息槽位
    check(InName != NAME_None && InternalIndex >= 0);
    if (InternalFlagsToSet != EInternalObjectFlags::None)
    {
        GUObjectArray.IndexToObject(InternalIndex)->SetFlags(InternalFlagsToSet);
    
    }   
    HashObject(this);   // Hash该对象
    check(IsValidLowLevel());
}

关键数据结构

  • FUObjectArray
  • FUObjectItem
/**
* Single item in the UObject array.
*/
struct FUObjectItem
{
    // Pointer to the allocated object
    class UObjectBase* Object;
    // Internal flags
    int32 Flags;
    // UObject Owner Cluster Index
    int32 ClusterRootIndex; 
    // Weak Object Pointer Serial number associated with the object
    int32 SerialNumber;   //该SerialNumber用于验证WeakPointer指向的对象已经被回收,槽位被新对象占用

    FUObjectItem()
        : Object(nullptr)
        , Flags(0)
        , ClusterRootIndex(0)
        , SerialNumber(0)
    {
    }
}

HashObject

void HashObject(UObjectBase* Object)
{
    SCOPE_CYCLE_COUNTER( STAT_Hash_HashObject );

    FName Name = Object->GetFName();
    if (Name != NAME_None)
    {
        int32 Hash = 0;

        auto& ThreadHash = FUObjectHashTables::Get();
        FHashTableLock HashLock(ThreadHash);

        Hash = GetObjectHash(Name);             
        checkSlow(!ThreadHash.PairExistsInHash(Hash, Object));  // if it already exists, something is wrong with the external code
        ThreadHash.AddToHash(Hash, Object);

        Hash = GetObjectOuterHash( Name, (PTRINT)Object->GetOuter() );
        checkSlow( !ThreadHash.HashOuter.FindPair( Hash, Object ) );  // if it already exists, something is wrong with the external code
        ThreadHash.HashOuter.Add( Hash, Object );

        AddToOuterMap( ThreadHash, Object );
        AddToClassMap( ThreadHash, Object );
    }
}

FindObject

编写调试代码如下:

void AFirstPersonCPPCharacter::OnFire()
{
    UPlayerData *PlayerData = NewObject(this, FName(TEXT("Jackson")));
    check(PlayerData);

    FString ObjPathname = PlayerData->GetPathName();
    UPackage *Package = PlayerData->GetOutermost();
    UPlayerData* FindResult = FindObject(nullptr, ObjPathname, false);
    check(FindResult);
}

ObjPathname值为/Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.FirstPersonCharacter_C_0.Jackson

//
// Find an optional object.
//
UObject* StaticFindObject( UClass* ObjectClass, UObject* InObjectPackage, const TCHAR* OrigInName, bool ExactClass )
{
    INC_DWORD_STAT(STAT_FindObject);

    if (GIsSavingPackage)
    {
        UE_LOG(LogUObjectGlobals, Fatal,TEXT("Illegal call to StaticFindObject() while serializing object data!"));
    }

    if (IsGarbageCollectingOnGameThread())
    {
        UE_LOG(LogUObjectGlobals, Fatal,TEXT("Illegal call to StaticFindObject() while collecting garbage!"));
    }

    // Resolve the object and package name.
    const bool bAnyPackage = InObjectPackage==ANY_PACKAGE;
    UObject* ObjectPackage = bAnyPackage ? nullptr : InObjectPackage;

    UObject* MatchingObject = nullptr;

#if WITH_EDITOR
    // If the editor is running, and T3D is being imported, ensure any packages referenced are fully loaded.
    if ((GIsEditor == true) && (GIsImportingT3D == true))// && (ObjectPackage != ANY_PACKAGE) && (ObjectPackage != NULL))
    {
        static bool s_bCurrentlyLoading = false;

        if (s_bCurrentlyLoading == false)
        {
            FString NameCheck = OrigInName;
            if (NameCheck.Contains(TEXT("."), ESearchCase::CaseSensitive) && 
                !NameCheck.Contains(TEXT("'"), ESearchCase::CaseSensitive) && 
                !NameCheck.Contains(TEXT(":"), ESearchCase::CaseSensitive) )
            {
                s_bCurrentlyLoading = true;
                MatchingObject = StaticLoadObject(ObjectClass, nullptr, OrigInName, nullptr,  LOAD_NoWarn, nullptr);
                s_bCurrentlyLoading = false;
                if (MatchingObject != nullptr)
                {
                    return MatchingObject;
                }
            }
        }
    }
#endif  //#if !WITH_EDITOR

    FName ObjectName;

    // Don't resolve the name if we're searching in any package
    if (!bAnyPackage)
    {
        FString InName = OrigInName;
        if (!ResolveName(ObjectPackage, InName, false, false)) // 解析出最终对象shortname和它的Outer对象.  这个类似文件系统的文件查找,会递归调用FindObject
        {
            return nullptr;
        }
        ObjectName = FName(*InName, FNAME_Add);
    }
    else
    {
        ObjectName = FName(OrigInName, FNAME_Add);
    }

        // Find Object Fast
    return StaticFindObjectFast(ObjectClass, ObjectPackage, ObjectName, ExactClass, bAnyPackage);
}
UE4对象系统_对象的创建和查找_第2张图片
Paste_Image.png
UObject* StaticFindObjectFastInternal(UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags, EInternalObjectFlags ExclusiveInternalFlags)
{
    SCOPE_CYCLE_COUNTER( STAT_Hash_StaticFindObjectFastInternal );
    INC_DWORD_STAT(STAT_FindObjectFast);

    check(ObjectPackage != ANY_PACKAGE); // this could never have returned anything but nullptr
    // If they specified an outer use that during the hashing
    auto& ThreadHash = FUObjectHashTables::Get();
    UObject* Result = StaticFindObjectFastInternalThreadSafe(ThreadHash, ObjectClass, ObjectPackage, ObjectName, bExactClass, bAnyPackage, ExcludeFlags | RF_NewerVersionExists, ExclusiveInternalFlags);
    return Result;
}
UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags, EInternalObjectFlags ExclusiveInternalFlags)
{
    // If they specified an outer use that during the hashing
    UObject* Result = nullptr;
    if (ObjectPackage != nullptr)
    {
        int32 Hash = GetObjectOuterHash(ObjectName, (PTRINT)ObjectPackage);
        FHashTableLock HashLock(ThreadHash);    // 加锁
        for (TMultiMap::TConstKeyIterator HashIt(ThreadHash.HashOuter, Hash); HashIt; ++HashIt)
        {
            UObject *Object = (UObject *)HashIt.Value();
            if
                /* check that the name matches the name we're searching for */
                ((Object->GetFName() == ObjectName)

                /* Don't return objects that have any of the exclusive flags set */
                && !Object->HasAnyFlags(ExcludeFlags)

                /* check that the object has the correct Outer */
                && Object->GetOuter() == ObjectPackage

                /** If a class was specified, check that the object is of the correct class */
                && (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass)))
                
                /** Include (or not) pending kill objects */
                && !Object->HasAnyInternalFlags(ExclusiveInternalFlags))
            {
                checkf(!Object->IsUnreachable(), TEXT("%s"), *Object->GetFullName());
                if (Result)
                {
                    UE_LOG(LogUObjectHash, Warning, TEXT("Ambiguous search, could be %s or %s"), *GetFullNameSafe(Result), *GetFullNameSafe(Object));
                }
                else
                {
                    Result = Object;
                }
#if (UE_BUILD_SHIPPING || UE_BUILD_TEST)
                break;
#endif
            }
        }
    }
    else
    {
        // Find an object with the specified name and (optional) class, in any package; if bAnyPackage is false, only matches top-level packages
        FName ActualObjectName = ObjectName;
        const FString ObjectNameString = ObjectName.ToString();
        const int32 DotIndex = FMath::Max(ObjectNameString.Find(TEXT("."), ESearchCase::CaseSensitive, ESearchDir::FromEnd),
            ObjectNameString.Find(TEXT(":"), ESearchCase::CaseSensitive, ESearchDir::FromEnd));
        if (DotIndex != INDEX_NONE)
        {
            ActualObjectName = FName(*ObjectNameString.Mid(DotIndex + 1));
        }
        const int32 Hash = GetObjectHash(ActualObjectName);
        FHashTableLock HashLock(ThreadHash);

        FHashBucket* Bucket = ThreadHash.Hash.Find(Hash);
        if (Bucket)
        {
            for (FHashBucketIterator It(*Bucket); It; ++It)
            {
                UObject* Object = (UObject*)*It;
                if
                    ((Object->GetFName() == ActualObjectName)

                    /* Don't return objects that have any of the exclusive flags set */
                    && !Object->HasAnyFlags(ExcludeFlags)

                    /*If there is no package (no InObjectPackage specified, and InName's package is "")
                    and the caller specified any_package, then accept it, regardless of its package.
                    Or, if the object is a top-level package then accept it immediately.*/
                    && (bAnyPackage || !Object->GetOuter())

                    /** If a class was specified, check that the object is of the correct class */
                    && (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass)))

                    /** Include (or not) pending kill objects */
                    && !Object->HasAnyInternalFlags(ExclusiveInternalFlags)

                    /** Ensure that the partial path provided matches the object found */
                    && (Object->GetPathName().EndsWith(ObjectNameString)))
                {
                    checkf(!Object->IsUnreachable(), TEXT("%s"), *Object->GetFullName());
                    if (Result)
                    {
                        UE_LOG(LogUObjectHash, Warning, TEXT("Ambiguous search, could be %s or %s"), *GetFullNameSafe(Result), *GetFullNameSafe(Object));
                    }
                    else
                    {
                        Result = Object;
                    }
#if (UE_BUILD_SHIPPING || UE_BUILD_TEST)
                    break;
#endif
                }
            }
        }
    }
    // Not found.
    return Result;
}

小结
对象的查找就像操作系统中根据文件路径查找文件一样,一层一层剖洋葱一样。
关于HashObject的数据结构,暂不研究。
关于FObjectInstancingGraph这块代码太复杂了,本人还没有理解(UE3时就没有理解),所以暂时放过。

你可能感兴趣的:(UE4对象系统_对象的创建和查找)