PART 3 · 강의 3/4

AddReferencedObjects

FGCObject와 커스텀 GC 참조 추가로 Non-UObject 컨테이너에서 UObject를 안전하게 관리합니다

SECTION 01

FGCObject 기본 패턴

Non-UObject 클래스에서 UObject를 GC-safe하게 참조하는 방법

FGCObject 상속

FGCObject는 UObject가 아닌 클래스가 UObject를 참조할 때 GC에게 알리는 메커니즘입니다. AddReferencedObjects를 오버라이드하여 보호할 UObject 목록을 GC에 등록합니다.

C++ // FGCObject를 상속하여 GC 참조 등록 class FMyManager : public FGCObject { public: // 이 클래스가 참조하는 UObject들을 GC에 등록 virtual void AddReferencedObjects( FReferenceCollector& Collector) override { // 단일 참조 등록 Collector.AddReferencedObject(ManagedWidget); // 배열 참조 등록 Collector.AddReferencedObjects(ManagedActors); // TMap의 Value 등록 for (auto& Pair : DataMap) { Collector.AddReferencedObject(Pair.Value); } } // GC 디버깅용 이름 virtual FString GetReferencerName() const override { return TEXT("FMyManager"); } private: UUserWidget* ManagedWidget = nullptr; TArray<AActor*> ManagedActors; TMap<FName, UDataAsset*> DataMap; };
FGCObject 수명 관리

FGCObject 인스턴스 자체는 GC가 관리하지 않습니다. 수명은 C++ 규칙을 따릅니다. FGCObject가 파괴되면 자동으로 GC 참조 등록이 해제됩니다. 그러나 FGCObject가 파괴되지 않으면 등록된 UObject는 영원히 GC되지 않아 메모리 누수가 됩니다.

SECTION 02

UObject의 AddReferencedObjects

UObject 내부에서 UPROPERTY 없이 참조를 등록하는 방법

static AddReferencedObjects 패턴

UObject 파생 클래스에서도 UPROPERTY 없이 raw 포인터로 보관한 UObject를 GC에 등록할 수 있습니다. static 함수로 선언해야 합니다.

C++ UCLASS() class UMyObject : public UObject { GENERATED_BODY() public: // static 함수로 선언 (UHT가 자동으로 토큰 스트림에 등록) static void AddReferencedObjects( UObject* InThis, FReferenceCollector& Collector) { UMyObject* This = CastChecked<UMyObject>(InThis); // UPROPERTY가 아닌 참조를 수동 등록 Collector.AddReferencedObject(This->HiddenRef); // 커스텀 컨테이너 내 참조 등록 for (UObject*& Ref : This->CustomContainer) { Collector.AddReferencedObject(Ref); } // 부모 클래스 호출 (중요!) Super::AddReferencedObjects(InThis, Collector); } private: // UPROPERTY 없는 참조 - AddReferencedObjects로 보호 UObject* HiddenRef = nullptr; // 커스텀 컨테이너 (std::vector 등) std::vector<UObject*> CustomContainer; };
언제 AddReferencedObjects를 사용하는가?

대부분의 경우 UPROPERTY()로 충분합니다. AddReferencedObjects는 다음과 같은 특수한 상황에서만 사용합니다:

  • STL 컨테이너(std::vector, std::unordered_map)에 UObject 참조 저장
  • 커스텀 메모리 풀에서 UObject 참조 관리
  • Native C++ 라이브러리와의 인터페이스
  • 동적으로 생성되는 참조 구조 (그래프 등)
SECTION 03

FReferenceCollector API

참조 수집기의 다양한 등록 메서드

FReferenceCollector 주요 메서드

C++ class FReferenceCollector { public: // 단일 UObject 참조 등록 template<typename T> void AddReferencedObject(T*& Object); // TArray<UObject*> 전체 등록 template<typename T> void AddReferencedObjects(TArray<T*>& Objects); // TSet<UObject*> 전체 등록 template<typename T> void AddReferencedObjects(TSet<T*>& Objects); // TMap에서 Value가 UObject*인 경우 template<typename K, typename V> void AddReferencedObjects(TMap<K, V*>& Map); // 조건부 참조 (null이면 건너뜀) void AddReferencedObject(UObject*& Object) { if (Object) { HandleObjectReference(Object); } } // 소유자 정보 포함 (디버깅용) void AddPropertyReference( UObject*& Object, const FProperty* Property); };
참조 변수는 반드시 레퍼런스(&)로 전달

AddReferencedObject의 매개변수가 포인터의 레퍼런스(T*&)인 이유는, GC가 객체를 이동시키거나 null로 설정할 수 있기 때문입니다. 값으로 전달하면 원본 포인터가 업데이트되지 않아 댕글링 포인터가 됩니다.

SECTION 04

FGCObject 실전 패턴

실전에서 자주 사용되는 FGCObject 패턴들

서브시스템 매니저 패턴

C++ // 게임 서브시스템의 Non-UObject 매니저 class FUIManager : public FGCObject { public: void PushWidget(UUserWidget* Widget) { WidgetStack.Push(Widget); } void PopWidget() { if (WidgetStack.Num() > 0) { UUserWidget* Top = WidgetStack.Pop(); Top->RemoveFromParent(); // Top은 다음 GC에서 수집 가능 } } virtual void AddReferencedObjects( FReferenceCollector& Collector) override { Collector.AddReferencedObjects(WidgetStack); Collector.AddReferencedObject(OverlayWidget); } virtual FString GetReferencerName() const override { return TEXT("FUIManager"); } private: TArray<UUserWidget*> WidgetStack; UUserWidget* OverlayWidget = nullptr; };
SUMMARY

핵심 요약

이 강의에서 배운 내용
  • FGCObject는 Non-UObject 클래스가 UObject 참조를 GC에 등록하는 메커니즘입니다
  • UObject의 static AddReferencedObjects는 UPROPERTY 없는 참조를 수동 등록합니다
  • FReferenceCollector는 포인터 레퍼런스(T*&)를 받아 GC가 포인터를 업데이트할 수 있게 합니다
  • GetReferencerName()을 구현하면 GC 디버깅 시 참조 출처를 추적할 수 있습니다
  • 대부분의 경우 UPROPERTY()로 충분하며, AddReferencedObjects는 특수 상황에만 사용합니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: AddReferencedObjects 기본 구현

UPROPERTY가 아닌 일반 멤버로 UObject*를 보유하는 클래스에서 static AddReferencedObjects를 구현하세요. FReferenceCollector::AddReferencedObject를 호출하여 GC가 해당 참조를 인식하게 만들고, GC 수집이 방지되는지 확인하세요.

실습 2: 컨테이너 내 UObject 참조 보호

TArray를 UPROPERTY가 아닌 일반 멤버로 보유하는 상황에서 AddReferencedObjects를 구현하여 배열 내 모든 객체를 GC에서 보호하세요. FReferenceCollector::AddReferencedObjects(배열 버전)의 사용법을 익히세요.

심화 과제: 복잡한 참조 구조의 GC 안전성 확보

TMap과 중첩 구조체 내부의 UObject* 참조를 AddReferencedObjects로 보호하는 패턴을 구현하세요. FGCObject를 상속받아 비-UObject 클래스에서 GC 참조를 관리하는 방법도 함께 구현하세요.