class COREUOBJECT_API FUObjectArray //UObject
{
public:
enum ESerialNumberConstants
{
START_SERIAL_NUMBER = 1000,
};
/**
* Base class for UObjectBase create class listeners
*/
class FUObjectCreateListener
{
public:
virtual ~FUObjectCreateListener() {}
/**
* Provides notification that a UObjectBase has been added to the uobject array
*
* @param Object object that has been destroyed
* @param Index index of object that is being deleted
*/
virtual void NotifyUObjectCreated(const class UObjectBase *Object, int32 Index)=0;
/**
* Called when UObject Array is being shut down, this is where all listeners should be removed from it
*/
virtual void OnUObjectArrayShutdown()=0;
};
/**
* Base class for UObjectBase delete class listeners
*/
class FUObjectDeleteListener
{
public:
virtual ~FUObjectDeleteListener() {}
/**
* Provides notification that a UObjectBase has been removed from the uobject array
*
* @param Object object that has been destroyed
* @param Index index of object that is being deleted
*/
virtual void NotifyUObjectDeleted(const class UObjectBase *Object, int32 Index)=0;
/**
* Called when UObject Array is being shut down, this is where all listeners should be removed from it
*/
virtual void OnUObjectArrayShutdown() = 0;
};
/**
* Constructor, initializes to no permanent object pool
*/
FUObjectArray();
/**
* Allocates and initializes the permanent object pool
*
* @param MaxUObjects maximum number of UObjects that can ever exist in the array
* @param MaxObjectsNotConsideredByGC number of objects in the permanent object pool
*/
void AllocateObjectPool(int32 MaxUObjects, int32 MaxObjectsNotConsideredByGC, bool bPreAllocateObjectArray);
/**
* Disables the disregard for GC optimization.
*
*/
void DisableDisregardForGC();
/**
* If there's enough slack in the disregard pool, we can re-open it and keep adding objects to it
*/
void OpenDisregardForGC();
/**
* After the initial load, this closes the disregard pool so that new object are GC-able
*/
void CloseDisregardForGC();
/** Returns true if the disregard for GC pool is open */
bool IsOpenForDisregardForGC() const
{
return OpenForDisregardForGC;
}
/**
* indicates if the disregard for GC optimization is active
*
* @return true if MaxObjectsNotConsideredByGC is greater than zero; this indicates that the disregard for GC optimization is enabled
*/
bool DisregardForGCEnabled() const
{
return MaxObjectsNotConsideredByGC > 0;
}
/**
* Adds a uobject to the global array which is used for uobject iteration
*
* @param Object Object to allocate an index for
*/
void AllocateUObjectIndex(class UObjectBase* Object, bool bMergingThreads = false);
/**
* Returns a UObject index top to the global uobject array
*
* @param Object object to free
*/
void FreeUObjectIndex(class UObjectBase* Object);
/**
* Returns the index of a UObject. Be advised this is only for very low level use.
*
* @param Object object to get the index of
* @return index of this object
*/
FORCEINLINE int32 ObjectToIndex(const class UObjectBase* Object) const
{
return Object->InternalIndex;
}
/**
* Returns the UObject corresponding to index. Be advised this is only for very low level use.
*
* @param Index index of object to return
* @return Object at this index
*/
FORCEINLINE FUObjectItem* IndexToObject(int32 Index)
{
check(Index >= 0);
if (Index < ObjObjects.Num())
{
return const_cast<FUObjectItem*>(&ObjObjects[Index]);
}
return nullptr;
}
FORCEINLINE FUObjectItem* IndexToObjectUnsafeForGC(int32 Index)
{
return const_cast<FUObjectItem*>(&ObjObjects[Index]);
}
FORCEINLINE FUObjectItem* IndexToObject(int32 Index, bool bEvenIfPendingKill)
{
FUObjectItem* ObjectItem = IndexToObject(Index);
if (ObjectItem && ObjectItem->Object)
{
if (!bEvenIfPendingKill && ObjectItem->IsPendingKill())
{
ObjectItem = nullptr;;
}
}
return ObjectItem;
}
FORCEINLINE FUObjectItem* ObjectToObjectItem(UObjectBase* Object)
{
FUObjectItem* ObjectItem = IndexToObject(Object->InternalIndex);
return ObjectItem;
}
FORCEINLINE bool IsValid(FUObjectItem* ObjectItem, bool bEvenIfPendingKill)
{
if (ObjectItem)
{
return bEvenIfPendingKill ? !ObjectItem->IsUnreachable() : !(ObjectItem->IsUnreachable() || ObjectItem->IsPendingKill());
}
return false;
}
FORCEINLINE FUObjectItem* IndexToValidObject(int32 Index, bool bEvenIfPendingKill)
{
FUObjectItem* ObjectItem = IndexToObject(Index);
return IsValid(ObjectItem, bEvenIfPendingKill) ? ObjectItem : nullptr;
}
FORCEINLINE bool IsValid(int32 Index, bool bEvenIfPendingKill)
{
// This method assumes Index points to a valid object.
FUObjectItem* ObjectItem = IndexToObject(Index);
return IsValid(ObjectItem, bEvenIfPendingKill);
}
FORCEINLINE bool IsStale(FUObjectItem* ObjectItem, bool bEvenIfPendingKill)
{
// This method assumes ObjectItem is valid.
return bEvenIfPendingKill ? (ObjectItem->IsPendingKill() || ObjectItem->IsUnreachable()) : (ObjectItem->IsUnreachable());
}
FORCEINLINE bool IsStale(int32 Index, bool bEvenIfPendingKill)
{
// This method assumes Index points to a valid object.
FUObjectItem* ObjectItem = IndexToObject(Index);
if (ObjectItem)
{
return IsStale(ObjectItem, bEvenIfPendingKill);
}
return true;
}
/** Returns the index of the first object outside of the disregard for GC pool */
FORCEINLINE int32 GetFirstGCIndex() const
{
return ObjFirstGCIndex;
}
/**
* Adds a new listener for object creation
*
* @param Listener listener to notify when an object is deleted
*/
void AddUObjectCreateListener(FUObjectCreateListener* Listener);
/**
* Removes a listener for object creation
*
* @param Listener listener to remove
*/
void RemoveUObjectCreateListener(FUObjectCreateListener* Listener);
/**
* Adds a new listener for object deletion
*
* @param Listener listener to notify when an object is deleted
*/
void AddUObjectDeleteListener(FUObjectDeleteListener* Listener);
/**
* Removes a listener for object deletion
*
* @param Listener listener to remove
*/
void RemoveUObjectDeleteListener(FUObjectDeleteListener* Listener);
/**
* Checks if a UObject pointer is valid
*
* @param Object object to test for validity
* @return true if this index is valid
*/
bool IsValid(const UObjectBase* Object) const;
/** Checks if the object index is valid. */
FORCEINLINE bool IsValidIndex(const UObjectBase* Object) const
{
return ObjObjects.IsValidIndex(Object->InternalIndex);
}
/**
* Returns true if this object is "disregard for GC"...same results as the legacy RF_DisregardForGC flag
*
* @param Object object to get for disregard for GC
* @return true if this object si disregard for GC
*/
FORCEINLINE bool IsDisregardForGC(const class UObjectBase* Object)
{
return Object->InternalIndex <= ObjLastNonGCIndex;
}
/**
* Returns the size of the global UObject array, some of these might be unused
*
* @return the number of UObjects in the global array
*/
FORCEINLINE int32 GetObjectArrayNum() const
{
return ObjObjects.Num();
}
/**
* Returns the size of the global UObject array minus the number of permanent objects
*
* @return the number of UObjects in the global array
*/
FORCEINLINE int32 GetObjectArrayNumMinusPermanent() const
{
return ObjObjects.Num() - (ObjLastNonGCIndex + 1);
}
/**
* Returns the number of permanent objects
*
* @return the number of permanent objects
*/
FORCEINLINE int32 GetObjectArrayNumPermanent() const
{
return ObjLastNonGCIndex + 1;
}
#if UE_GC_TRACK_OBJ_AVAILABLE
/**
* Returns the number of actual object indices that are claimed (the total size of the global object array minus
* the number of available object array elements
*
* @return The number of objects claimed
*/
int32 GetObjectArrayNumMinusAvailable()
{
return ObjObjects.Num() - ObjAvailableCount.GetValue();
}
/**
* Returns the estimated number of object indices available for allocation
*/
int32 GetObjectArrayEstimatedAvailable()
{
return ObjObjects.Capacity() - GetObjectArrayNumMinusAvailable();
}
#endif
/**
* Clears some internal arrays to get rid of false memory leaks
*/
void ShutdownUObjectArray();
/**
* Given a UObject index return the serial number. If it doesn't have a serial number, give it one. Threadsafe.
* @param Index - UObject Index
* @return - the serial number for this UObject
*/
int32 AllocateSerialNumber(int32 Index);
/**
* Given a UObject index return the serial number. If it doesn't have a serial number, return 0. Threadsafe.
* @param Index - UObject Index
* @return - the serial number for this UObject
*/
FORCEINLINE int32 GetSerialNumber(int32 Index)
{
FUObjectItem* ObjectItem = IndexToObject(Index);
checkSlow(ObjectItem);
return ObjectItem->GetSerialNumber();
}
/**
* Low level iterator.
*/
class TIterator
{
public:
enum EEndTagType
{
EndTag
};
/**
* Constructor
*
* @param InArray the array to iterate on
* @param bOnlyGCedObjects if true, skip all of the permanent objects
*/
TIterator( const FUObjectArray& InArray, bool bOnlyGCedObjects = false ) :
Array(InArray),
Index(-1),
CurrentObject(nullptr)
{
if (bOnlyGCedObjects)
{
Index = Array.ObjLastNonGCIndex;
}
Advance();
}
/**
* Constructor
*
* @param InArray the array to iterate on
* @param bOnlyGCedObjects if true, skip all of the permanent objects
*/
TIterator( EEndTagType, const TIterator& InIter ) :
Array (InIter.Array),
Index(Array.ObjObjects.Num())
{
}
/**
* Iterator advance
*/
FORCEINLINE void operator++()
{
Advance();
}
friend bool operator==(const TIterator& Lhs, const TIterator& Rhs) { return Lhs.Index == Rhs.Index; }
friend bool operator!=(const TIterator& Lhs, const TIterator& Rhs) { return Lhs.Index != Rhs.Index; }
/** Conversion to "bool" returning true if the iterator is valid. */
FORCEINLINE explicit operator bool() const
{
return !!CurrentObject;
}
/** inverse of the "bool" operator */
FORCEINLINE bool operator !() const
{
return !(bool)*this;
}
FORCEINLINE int32 GetIndex() const
{
return Index;
}
protected:
/**
* Dereferences the iterator with an ordinary name for clarity in derived classes
*
* @return the UObject at the iterator
*/
FORCEINLINE FUObjectItem* GetObject() const
{
return CurrentObject;
}
/**
* Iterator advance with ordinary name for clarity in subclasses
* @return true if the iterator points to a valid object, false if iteration is complete
*/
FORCEINLINE bool Advance()
{
//@todo UE4 check this for LHS on Index on consoles
FUObjectItem* NextObject = nullptr;
CurrentObject = nullptr;
while(++Index < Array.GetObjectArrayNum())
{
NextObject = const_cast<FUObjectItem*>(&Array.ObjObjects[Index]);
if (NextObject->Object)
{
CurrentObject = NextObject;
return true;
}
}
return false;
}
private:
/** the array that we are iterating on, probably always GUObjectArray */
const FUObjectArray& Array;
/** index of the current element in the object array */
int32 Index;
/** Current object */
mutable FUObjectItem* CurrentObject;
};
private:
//typedef TStaticIndirectArrayThreadSafeRead<UObjectBase, 8 * 1024 * 1024 /* Max 8M UObjects */, 16384 /* allocated in 64K/128K chunks */ > TUObjectArray;
typedef FChunkedFixedUObjectArray TUObjectArray;
// note these variables are left with the Obj prefix so they can be related to the historical GObj versions
/** First index into objects array taken into account for GC. */
int32 ObjFirstGCIndex;
/** Index pointing to last object created in range disregarded for GC. */
int32 ObjLastNonGCIndex;
/** Maximum number of objects in the disregard for GC Pool */
int32 MaxObjectsNotConsideredByGC;
/** If true this is the intial load and we should load objects int the disregarded for GC range. */
bool OpenForDisregardForGC;
/** Array of all live objects. */
TUObjectArray ObjObjects; //对象指针都存放在这里
/** Synchronization object for all live objects. */
FCriticalSection ObjObjectsCritical;
/** Available object indices. */
TLockFreePointerListUnordered<int32, PLATFORM_CACHE_LINE_SIZE> ObjAvailableList;
#if UE_GC_TRACK_OBJ_AVAILABLE
/** Available object index count. */
FThreadSafeCounter ObjAvailableCount;
#endif
/**
* Array of things to notify when a UObjectBase is created
*/
TArray<FUObjectCreateListener* > UObjectCreateListeners;
/**
* Array of things to notify when a UObjectBase is destroyed
*/
TArray<FUObjectDeleteListener* > UObjectDeleteListeners;
#if THREADSAFE_UOBJECTS
FCriticalSection UObjectDeleteListenersCritical;
#endif
/** Current master serial number **/
FThreadSafeCounter MasterSerialNumber;
public:
/** INTERNAL USE ONLY: gets the internal FUObjectItem array */
TUObjectArray& GetObjectItemArrayUnsafe()
{
return ObjObjects;
}
};
class FChunkedFixedUObjectArray
{
enum
{
NumElementsPerChunk = 64 * 1024, //所有 chunk 都是这个大小,便于计算偏移。
};
/** Master table to chunks of pointers **/
FUObjectItem** Objects; //指向指针数组的指针,每个元素是一个指向 FUObjectItem 数组(即“chunk”)的指针
/** If requested, a contiguous memory where all objects are allocated **/
FUObjectItem* PreAllocatedObjects;//备用
/** Maximum number of elements **/
int32 MaxElements; //当前支持的最大元素数量
/** Number of elements we currently have **/
int32 NumElements; //当前已使用的元素数量(即有效 UObject 数量)。
/** Maximum number of chunks **/
int32 MaxChunks; //最大 chunk 数量,由 MaxElements 计算得出。
/** Number of chunks we currently have **/
int32 NumChunks; //当前已分配的 chunk 数量。
/**
* Allocates new chunk for the array
**/
void ExpandChunksToIndex(int32 Index)
{
check(Index >= 0 && Index < MaxElements);
int32 ChunkIndex = Index / NumElementsPerChunk;
while (ChunkIndex >= NumChunks)
{
// add a chunk, and make sure nobody else tries
FUObjectItem** Chunk = &Objects[NumChunks];
FUObjectItem* NewChunk = new FUObjectItem[NumElementsPerChunk];
if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)Chunk, NewChunk, nullptr))
{
// someone else beat us to the add, we don't support multiple concurrent adds
check(0)
}
else
{
NumChunks++;
check(NumChunks <= MaxChunks);
}
}
check(ChunkIndex < NumChunks && Objects[ChunkIndex]); // should have a valid pointer now
}
public:
/** Constructor : Probably not thread safe **/
FChunkedFixedUObjectArray() TSAN_SAFE
: Objects(nullptr)
, PreAllocatedObjects(nullptr)
, MaxElements(0)
, NumElements(0)
, MaxChunks(0)
, NumChunks(0)
{
}
~FChunkedFixedUObjectArray()
{
if (!PreAllocatedObjects)
{
for (int32 ChunkIndex = 0; ChunkIndex < MaxChunks; ++ChunkIndex)
{
delete[] Objects[ChunkIndex];
}
}
else
{
delete[] PreAllocatedObjects;
}
delete[] Objects;
}
/**
* Expands the array so that Element[Index] is allocated. New pointers are all zero.
* @param Index The Index of an element we want to be sure is allocated
**/
void PreAllocate(int32 InMaxElements, bool bPreAllocateChunks) TSAN_SAFE
{
check(!Objects);
MaxChunks = InMaxElements / NumElementsPerChunk + 1;
MaxElements = MaxChunks * NumElementsPerChunk;
Objects = new FUObjectItem*[MaxChunks];
FMemory::Memzero(Objects, sizeof(FUObjectItem*) * MaxChunks);
if (bPreAllocateChunks)
{
// Fully allocate all chunks as contiguous memory
PreAllocatedObjects = new FUObjectItem[MaxElements];
for (int32 ChunkIndex = 0; ChunkIndex < MaxChunks; ++ChunkIndex)
{
Objects[ChunkIndex] = PreAllocatedObjects + ChunkIndex * NumElementsPerChunk;
}
NumChunks = MaxChunks;
}
}
/**
* Return the number of elements in the array
* Thread safe, but you know, someone might have added more elements before this even returns
* @return the number of elements in the array
**/
FORCEINLINE int32 Num() const
{
return NumElements;
}
/**
* Return the number max capacity of the array
* Thread safe, but you know, someone might have added more elements before this even returns
* @return the maximum number of elements in the array
**/
FORCEINLINE int32 Capacity() const TSAN_SAFE
{
return MaxElements;
}
/**
* Return if this index is valid
* Thread safe, if it is valid now, it is valid forever. Other threads might be adding during this call.
* @param Index Index to test
* @return true, if this is a valid
**/
FORCEINLINE bool IsValidIndex(int32 Index) const
{
return Index < Num() && Index >= 0;
}
/**
* Return a pointer to the pointer to a given element
* @param Index The Index of an element we want to retrieve the pointer-to-pointer for
**/
FORCEINLINE_DEBUGGABLE FUObjectItem const* GetObjectPtr(int32 Index) const TSAN_SAFE
{
const int32 ChunkIndex = Index / NumElementsPerChunk;
const int32 WithinChunkIndex = Index % NumElementsPerChunk;
checkf(IsValidIndex(Index), TEXT("IsValidIndex(%d)"), Index);
checkf(ChunkIndex < NumChunks, TEXT("ChunkIndex (%d) < NumChunks (%d)"), ChunkIndex, NumChunks);
checkf(Index < MaxElements, TEXT("Index (%d) < MaxElements (%d)"), Index, MaxElements);
FUObjectItem* Chunk = Objects[ChunkIndex];
check(Chunk);
return Chunk + WithinChunkIndex;
}
FORCEINLINE_DEBUGGABLE FUObjectItem* GetObjectPtr(int32 Index) TSAN_SAFE
{
const int32 ChunkIndex = Index / NumElementsPerChunk;
const int32 WithinChunkIndex = Index % NumElementsPerChunk;
checkf(IsValidIndex(Index), TEXT("IsValidIndex(%d)"), Index);
checkf(ChunkIndex < NumChunks, TEXT("ChunkIndex (%d) < NumChunks (%d)"), ChunkIndex, NumChunks);
checkf(Index < MaxElements, TEXT("Index (%d) < MaxElements (%d)"), Index, MaxElements);
FUObjectItem* Chunk = Objects[ChunkIndex];
check(Chunk);
return Chunk + WithinChunkIndex;
}
/**
* Return a reference to an element
* @param Index Index to return
* @return a reference to the pointer to the element
* Thread safe, if it is valid now, it is valid forever. This might return nullptr, but by then, some other thread might have made it non-nullptr.
**/
FORCEINLINE FUObjectItem const& operator[](int32 Index) const
{
FUObjectItem const* ItemPtr = GetObjectPtr(Index);
check(ItemPtr);
return *ItemPtr;
}
FORCEINLINE FUObjectItem& operator[](int32 Index)
{
FUObjectItem* ItemPtr = GetObjectPtr(Index);
check(ItemPtr);
return *ItemPtr;
}
int32 AddRange(int32 NumToAdd) TSAN_SAFE
{
int32 Result = NumElements;
checkf(Result + NumToAdd <= MaxElements, TEXT("Maximum number of UObjects (%d) exceeded, make sure you update MaxObjectsInGame/MaxObjectsInEditor in project settings."), MaxElements);
ExpandChunksToIndex(Result + NumToAdd - 1);
NumElements += NumToAdd;
return Result;
}
int32 AddSingle() TSAN_SAFE
{
return AddRange(1);
}
/**
* Return a naked pointer to the fundamental data structure for debug visualizers.
**/
FUObjectItem*** GetRootBlockForDebuggerVisualizers()
{
return nullptr;
}
};
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;
FUObjectItem()
: Object(nullptr)
, Flags(0)
, ClusterRootIndex(0)
, SerialNumber(0)
{
}
FORCEINLINE void SetOwnerIndex(int32 OwnerIndex)
{
ClusterRootIndex = OwnerIndex;
}
FORCEINLINE int32 GetOwnerIndex() const
{
return ClusterRootIndex;
}
/** Encodes the cluster index in the ClusterRootIndex variable */
FORCEINLINE void SetClusterIndex(int32 ClusterIndex)
{
ClusterRootIndex = -ClusterIndex - 1;
}
/** Decodes the cluster index from the ClusterRootIndex variable */
FORCEINLINE int32 GetClusterIndex() const
{
checkSlow(ClusterRootIndex < 0);
return -ClusterRootIndex - 1;
}
FORCEINLINE int32 GetSerialNumber() const
{
return SerialNumber;
}
FORCEINLINE void SetFlags(EInternalObjectFlags FlagsToSet)
{
check((int32(FlagsToSet) & ~int32(EInternalObjectFlags::AllFlags)) == 0);
Flags |= int32(FlagsToSet);
}
FORCEINLINE EInternalObjectFlags GetFlags() const
{
return EInternalObjectFlags(Flags);
}
FORCEINLINE void ClearFlags(EInternalObjectFlags FlagsToClear)
{
check((int32(FlagsToClear) & ~int32(EInternalObjectFlags::AllFlags)) == 0);
Flags &= ~int32(FlagsToClear);
}
/**
* Uses atomics to clear the specified flag(s).
* @param FlagsToClear
* @return True if this call cleared the flag, false if it has been cleared by another thread.
*/
FORCEINLINE bool ThisThreadAtomicallyClearedFlag(EInternalObjectFlags FlagToClear)
{
static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics.");
bool bIChangedIt = false;
while (1)
{
int32 StartValue = int32(Flags);
if (!(StartValue & int32(FlagToClear)))
{
break;
}
int32 NewValue = StartValue & ~int32(FlagToClear);
checkSlow(NewValue != StartValue);
if ((int32)FPlatformAtomics::InterlockedCompareExchange((int32*)&Flags, NewValue, StartValue) == StartValue)
{
bIChangedIt = true;
break;
}
}
return bIChangedIt;
}
FORCEINLINE bool ThisThreadAtomicallySetFlag(EInternalObjectFlags FlagToSet)
{
static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics.");
bool bIChangedIt = false;
while (1)
{
int32 StartValue = int32(Flags);
if (StartValue & int32(FlagToSet))
{
break;
}
int32 NewValue = StartValue | int32(FlagToSet);
checkSlow(NewValue != StartValue);
if ((int32)FPlatformAtomics::InterlockedCompareExchange((int32*)&Flags, NewValue, StartValue) == StartValue)
{
bIChangedIt = true;
break;
}
}
return bIChangedIt;
}
FORCEINLINE bool HasAnyFlags(EInternalObjectFlags InFlags) const
{
return !!(Flags & int32(InFlags));
}
FORCEINLINE void SetUnreachable()
{
Flags |= int32(EInternalObjectFlags::Unreachable);
}
FORCEINLINE void ClearUnreachable()
{
Flags &= ~int32(EInternalObjectFlags::Unreachable);
}
FORCEINLINE bool IsUnreachable() const
{
return !!(Flags & int32(EInternalObjectFlags::Unreachable));
}
FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable()
{
return ThisThreadAtomicallyClearedFlag(EInternalObjectFlags::Unreachable);
}
FORCEINLINE void SetPendingKill()
{
Flags |= int32(EInternalObjectFlags::PendingKill);
}
FORCEINLINE void ClearPendingKill()
{
Flags &= ~int32(EInternalObjectFlags::PendingKill);
}
FORCEINLINE bool IsPendingKill() const
{
return !!(Flags & int32(EInternalObjectFlags::PendingKill));
}
FORCEINLINE void SetRootSet()
{
Flags |= int32(EInternalObjectFlags::RootSet);
}
FORCEINLINE void ClearRootSet()
{
Flags &= ~int32(EInternalObjectFlags::RootSet);
}
FORCEINLINE bool IsRootSet() const
{
return !!(Flags & int32(EInternalObjectFlags::RootSet));
}
FORCEINLINE void ResetSerialNumberAndFlags()
{
Flags = 0;
ClusterRootIndex = 0;
SerialNumber = 0;
}
};
class COREUOBJECT_API UObjectBase
{
friend class UObjectBaseUtility;
friend struct Z_Construct_UClass_UObject_Statics;
friend class FUObjectArray; // for access to InternalIndex without revealing it to anyone else
friend class FUObjectAllocator; // for access to destructor without revealing it to anyone else
friend COREUOBJECT_API void UObjectForceRegistration(UObjectBase* Object, bool bCheckForModuleRelease);
friend COREUOBJECT_API void InitializePrivateStaticClass(
class UClass* TClass_Super_StaticClass,
class UClass* TClass_PrivateStaticClass,
class UClass* TClass_WithinClass_StaticClass,
const TCHAR* PackageName,
const TCHAR* Name
);
protected:
UObjectBase() :
NamePrivate(NoInit) // screwy, but the name was already set and we don't want to set it again
#if ENABLE_STATNAMEDEVENTS_UOBJECT
, StatIDStringStorage(nullptr)
#endif
{
}
/**
* Constructor used for bootstrapping
* @param InFlags RF_Flags to assign
*/
UObjectBase( EObjectFlags InFlags );
public:
/**
* 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 InInternalFlags EInternalObjectFlags to assign
* @param InOuter outer for this object
* @param InName name of the new object
*/
UObjectBase( UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName );
/**
* Final destructor, removes the object from the object array, and indirectly, from any annotations
**/
virtual ~UObjectBase();
/**
* Emit GC tokens for UObjectBase, this might be UObject::StaticClass or Default__Class
**/
static void EmitBaseReferences(UClass *RootClass);
protected:
/**
* Just change the FName and Outer and rehash into name hash tables. For use by higher level rename functions.
*
* @param NewName new name for this object
* @param NewOuter new outer for this object, if NULL, outer will be unchanged
*/
void LowLevelRename(FName NewName,UObject *NewOuter = NULL);
/** Force any base classes to be registered first */
virtual void RegisterDependencies() {}
/** Enqueue the registration for this object. */
void Register(const TCHAR* PackageName,const TCHAR* Name);
/**
* Convert a boot-strap registered class into a real one, add to uobject array, etc
*
* @param UClassStaticClass Now that it is known, fill in UClass::StaticClass() as the class
*/
virtual void DeferredRegister(UClass *UClassStaticClass,const TCHAR* PackageName,const TCHAR* Name);
private:
/**
* Add a newly created object to the name hash tables and the object array
*
* @param Name name to assign to this uobject
* @param InSetInternalFlags Internal object flags to be set on the object once it's been added to the array
*/
void AddObject(FName Name, EInternalObjectFlags InSetInternalFlags);
public:
/**
* Checks to see if the object appears to be valid
* @return true if this appears to be a valid object
*/
bool IsValidLowLevel() const;
/**
* Faster version of IsValidLowLevel.
* Checks to see if the object appears to be valid by checking pointers and their alignment.
* Name and InternalIndex checks are less accurate than IsValidLowLevel.
* @param bRecursive true if the Class pointer should be checked with IsValidLowLevelFast
* @return true if this appears to be a valid object
*/
bool IsValidLowLevelFast(bool bRecursive = true) const;
/**
* Returns the unique ID of the object...these are reused so it is only unique while the object is alive.
* Useful as a tag.
**/
FORCEINLINE uint32 GetUniqueID() const
{
return (uint32)InternalIndex;
}
/** Returns the UClass that defines the fields of this object */
FORCEINLINE UClass* GetClass() const
{
return ClassPrivate;
}
/** Returns the UObject this object resides in */
FORCEINLINE UObject* GetOuter() const
{
return OuterPrivate;
}
/** Returns the logical name of this object */
FORCEINLINE FName GetFName() const
{
return NamePrivate;
}
/**
* Returns the stat ID of the object, used for profiling. This will create a stat ID if needed.
*
* @param bForDeferred If true, a stat ID will be created even if a group is disabled
*/
FORCEINLINE TStatId GetStatID(bool bForDeferredUse = false) const
{
#if STATS
// this is done to avoid even registering stats for a disabled group (unless we plan on using it later)
if (bForDeferredUse || FThreadStats::IsCollectingData(GET_STATID(STAT_UObjectsStatGroupTester)))
{
if (!StatID.IsValidStat())
{
CreateStatID();
}
return StatID;
}
#elif ENABLE_STATNAMEDEVENTS_UOBJECT
if (!StatID.IsValidStat() && (bForDeferredUse || GCycleStatsShouldEmitNamedEvents))
{
CreateStatID();
}
return StatID;
#endif // STATS
return TStatId(); // not doing stats at the moment, or ever
}
private:
#if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT
/** Creates a stat ID for this object */
void CreateStatID() const;
#endif
protected:
/**
* Set the object flags directly
*
**/
FORCEINLINE void SetFlagsTo( EObjectFlags NewFlags )
{
checkfSlow((NewFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as 0x%x but is trying to set flags to RF_AllFlags"), *GetFName().ToString(), (int)ObjectFlags);
ObjectFlags = NewFlags;
}
public:
/**
* Retrieve the object flags directly
*
* @return Flags for this object
**/
FORCEINLINE EObjectFlags GetFlags() const
{
checkfSlow((ObjectFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as RF_AllFlags"), *GetFName().ToString());
return ObjectFlags;
}
/**
* Atomically adds the specified flags.
* Do not use unless you know what you are doing.
* Designed to be used only by parallel GC and UObject loading thread.
*/
FORCENOINLINE void AtomicallySetFlags( EObjectFlags FlagsToAdd )
{
int32 OldFlags = 0;
int32 NewFlags = 0;
do
{
OldFlags = ObjectFlags;
NewFlags = OldFlags | FlagsToAdd;
}
while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags );
}
/**
* Atomically clears the specified flags.
* Do not use unless you know what you are doing.
* Designed to be used only by parallel GC and UObject loading thread.
*/
FORCENOINLINE void AtomicallyClearFlags( EObjectFlags FlagsToClear )
{
int32 OldFlags = 0;
int32 NewFlags = 0;
do
{
OldFlags = ObjectFlags;
NewFlags = OldFlags & ~FlagsToClear;
}
while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags );
}
private:
/** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit
platforms to reduce memory waste */
EObjectFlags ObjectFlags;
/** Index into GObjectArray...very private. */
int32 InternalIndex;
/** Class the object belongs to. */
UClass* ClassPrivate;
/** Name of this object */
FName NamePrivate;
/** Object this object resides in. */
UObject* OuterPrivate;
#if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT
/** Stat id of this object, 0 if nobody asked for it yet */
mutable TStatId StatID;
#if ENABLE_STATNAMEDEVENTS_UOBJECT
mutable PROFILER_CHAR* StatIDStringStorage;
#endif
#endif // STATS || ENABLE_STATNAMEDEVENTS
friend class FBlueprintCompileReinstancer;
/** This is used by the reinstancer to re-class and re-archetype the current instances of a class before recompiling */
void SetClass(UClass* NewClass);
#if HACK_HEADER_GENERATOR
// Required by UHT makefiles for internal data serialization.
friend struct FObjectBaseArchiveProxy;
#endif // HACK_HEADER_GENERATOR
};
UObjectBase地址是不是与FUObjectArray地址一样
最新发布