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<UPlayerData>(this, FName(TEXT("Jackson")));
    check(PlayerData);
        ...
}

堆栈如下:

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<T*>(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<UPlayerData>(this, FName(TEXT("Jackson")));
    check(PlayerData);

    FString ObjPathname = PlayerData->GetPathName();
    UPackage *Package = PlayerData->GetOutermost();
    UPlayerData* FindResult = FindObject<UPlayerData>(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);
}
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<int32, class UObjectBase*>::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<int32>(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时就没有理解),所以暂时放过。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容