
Unreal Engine의 GAS의 GameplayEffectTypes.h 코드를 살펴보며 내부 동작 방식과 메모리 관리 측면에서 GC(Garbage Collection)와의 관계를 중점적으로 설명해 보겠습니다.
FGameplayEffectSpecHandle 구조체FGameplayEffectSpecHandle은 GameplayEffect를 안전하게 참조하고 관리하기 위한 핸들입니다. Unreal에서는 주로 UObject를 기반으로 한 객체가 GC의 영향을 받는데, FGameplayEffectSpec 같은 경우는 일반적인 객체로서 엔진의 GC 관리 대상이 아닙니다. 따라서, 스마트 포인터를 사용하여 메모리 관리를 직접 수행합니다.
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayEffectSpecHandle
{
GENERATED_USTRUCT_BODY()
FGameplayEffectSpecHandle();
FGameplayEffectSpecHandle(FGameplayEffectSpec* DataPtr);
/** Internal pointer to effect spec */
TSharedPtr<FGameplayEffectSpec> Data;
void Clear()
{
Data.Reset();
}
bool IsValid() const
{
return Data.IsValid();
}
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
/** Comparison operator */
bool operator==(FGameplayEffectSpecHandle const& Other) const
{
bool bBothValid = IsValid() && Other.IsValid();
bool bBothInvalid = !IsValid() && !Other.IsValid();
return (bBothInvalid || (bBothValid && (Data.Get() == Other.Data.Get())));
}
/** Comparison operator */
bool operator!=(FGameplayEffectSpecHandle const& Other) const
{
return !(FGameplayEffectSpecHandle::operator==(Other));
}
};
TSharedPtr<FGameplayEffectSpec> Data: Data는 FGameplayEffectSpec의 데이터를 가리키는 스마트 포인터입니다. 스마트 포인터를 사용하면 데이터가 더 이상 참조되지 않을 때 자동으로 메모리가 해제됩니다. 이를 통해 메모리 누수를 방지하며, Unreal의 GC 시스템 외부에서 객체의 수명을 관리할 수 있습니다.
Clear(): Data.Reset()을 호출하여 참조를 제거하고, 마지막 참조가 사라지면 메모리를 해제합니다.
IsValid(): Data가 유효한지 검사하는 함수로, FGameplayEffectSpec 데이터가 존재하는지 확인할 수 있습니다.
비교 연산자:
operator==: 두 FGameplayEffectSpecHandle 객체가 같은지 확인합니다. 유효한 두 포인터가 같은 메모리를 가리키는 경우에만 같은 것으로 판단합니다.operator!=: 위와 반대 논리로 비교합니다.이러한 구성은 GC에 의존하지 않는 객체 관리를 가능하게 하여, Unreal의 UObject가 아닌 일반 객체(FGameplayEffectSpec)를 안전하게 다룰 수 있게 해줍니다.
AddSourceObject 메서드이 함수는 GameplayEffect의 Source 객체를 추가합니다. Source 객체는 일반적으로 GameplayEffect를 발생시킨 주체(객체)이며, 이를 통해 효과가 어떤 객체에서 발생했는지 추적할 수 있습니다.
virtual void AddSourceObject(const UObject* NewSourceObject)
{
SourceObject = MakeWeakObjectPtr(const_cast<UObject*>(NewSourceObject));
bReplicateSourceObject = NewSourceObject && NewSourceObject->IsSupportedForNetworking();
}
MakeWeakObjectPtr(const_cast<UObject*>(NewSourceObject)): NewSourceObject를 TWeakObjectPtr으로 변환합니다. TWeakObjectPtr는 강한 참조가 아니므로 GC에서 객체의 수명에 영향을 주지 않으며, GC가 객체를 해제해도 안전합니다.
bReplicateSourceObject: NewSourceObject가 네트워크 동기화가 가능한 경우, 이 플래그를 설정하여 네트워크 리플리케이션에 필요한지 여부를 나타냅니다. 네트워크 게임에서 GameplayEffect의 동작을 클라이언트와 서버 간에 동기화할 때 유용합니다.
MakeWeakObjectPtr 템플릿 함수TWeakObjectPtr를 생성하는 편의 함수로, Unreal의 GC에서 관리되는 객체를 안전하게 참조할 때 사용됩니다. 강한 참조가 유지되지 않도록 설계되었기 때문에 GC가 객체를 자유롭게 해제할 수 있게 하여 메모리 관리를 돕습니다.
template <typename T>
FORCEINLINE TWeakObjectPtr<T> MakeWeakObjectPtr(T* Ptr)
{
return TWeakObjectPtr<T>(Ptr);
}
UObject 타입)를 약한 포인터로 변환하여 GC 안전성을 보장합니다. TWeakObjectPtr은 참조가 유지되지 않아 GC에서 객체의 수명을 제어할 수 있습니다. 따라서, 필요할 때만 객체가 유효한지 검사하여 사용할 수 있습니다.Unreal의 GAS의 GameplayEffectType은 안정적인 메모리 관리와 GC 호환성을 모두 만족시키는 방향으로 설계되었습니다. TSharedPtr은 UObject가 아닌 객체의 안전한 메모리 관리를 위해 사용되며, TWeakObjectPtr은 GC 관리 객체의 수명 주기 중에 약한 참조를 유지할 때 사용됩니다.