PART 3 ยท ๊ฐ•์˜ 2/4

Smart Pointers vs Raw

TWeakObjectPtr, TSoftObjectPtr, TObjectPtr์˜ GC ํŠน์„ฑ๊ณผ ์ ์ ˆํ•œ ์‚ฌ์šฉ ์‹œ์ ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค

SECTION 01

TObjectPtr - UE5์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋ณธ ํฌ์ธํ„ฐ

raw UObject*๋ฅผ ๋Œ€์ฒดํ•˜๋Š” UE5์˜ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ

TObjectPtr<T>

UE5์—์„œ ๋„์ž…๋œ TObjectPtr<T>๋Š” UPROPERTY์—์„œ raw UObject*๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ์—๋””ํ„ฐ ๋นŒ๋“œ์—์„œ๋Š” ์ง€์—ฐ ๋กœ๋”ฉ๊ณผ ์ ‘๊ทผ ์ถ”์ ์„ ์ œ๊ณตํ•˜๊ณ , ์‰ฌํ•‘ ๋นŒ๋“œ์—์„œ๋Š” raw ํฌ์ธํ„ฐ์™€ ๋™์ผํ•œ ์„ฑ๋Šฅ์„ ๋ณด์ž…๋‹ˆ๋‹ค.

C++ UCLASS() class AMyActor : public AActor { GENERATED_BODY() // UE5 ๋ฐฉ์‹ (๊ถŒ์žฅ) UPROPERTY() TObjectPtr<UStaticMeshComponent> MeshComp; // UE4 ๋ ˆ๊ฑฐ์‹œ ๋ฐฉ์‹ UPROPERTY() UStaticMeshComponent* OldStyleComp; // TObjectPtr๋Š” ์ž๋™์œผ๋กœ T*๋กœ ๋ณ€ํ™˜๋จ void UseComponent() { MeshComp->SetRelativeLocation(FVector::ZeroVector); // raw ํฌ์ธํ„ฐ์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉ UStaticMeshComponent* RawPtr = MeshComp.Get(); // ๋˜๋Š” ์•”์‹œ์  ๋ณ€ํ™˜ UStaticMeshComponent* RawPtr2 = MeshComp; } };

TObjectPtr์˜ GC ํŠน์„ฑ

ํŠน์„ฑTObjectPtrRaw UObject*
GC ์ฐธ์กฐ ์ถ”์ ๊ฐ•ํ•œ ์ฐธ์กฐ (UPROPERTY)๊ฐ•ํ•œ ์ฐธ์กฐ (UPROPERTY)
์“ฐ๊ธฐ ๋ฐฐ๋ฆฌ์–ด์ง€์› (Incremental GC)๋ฏธ์ง€์›
MarkAsGarbage ์ž๋™ ์ฒ˜๋ฆฌnull์ฒ˜๋Ÿผ ๋™์ž‘์ˆ˜๋™ ํ™•์ธ ํ•„์š”
์ง€์—ฐ ๋กœ๋”ฉ (์—๋””ํ„ฐ)์ง€์›๋ฏธ์ง€์›
์‰ฌํ•‘ ๋นŒ๋“œ ์˜ค๋ฒ„ํ—ค๋“œ์—†์Œ (raw์™€ ๋™์ผ)-
SECTION 02

TWeakObjectPtr - ์•ฝํ•œ ์ฐธ์กฐ

GC๋ฅผ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋Š” ์•ˆ์ „ํ•œ ์ฐธ์กฐ ๋ฐฉ์‹

TWeakObjectPtr<T>

TWeakObjectPtr์€ ๊ฐ์ฒด๋ฅผ GC๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜์ง€ ์•Š๋Š” ์•ฝํ•œ ์ฐธ์กฐ์ž…๋‹ˆ๋‹ค. ๋Œ€์ƒ ๊ฐ์ฒด๊ฐ€ GC๋˜๋ฉด ์ž๋™์œผ๋กœ null์ด ๋ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ GUObjectArray์˜ ์ธ๋ฑ์Šค์™€ SerialNumber๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

C++ // TWeakObjectPtr ์‚ฌ์šฉ๋ฒ• TWeakObjectPtr<AActor> WeakActor; // ํ• ๋‹น WeakActor = SomeActor; // ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (ํ•ต์‹ฌ!) if (WeakActor.IsValid()) { AActor* Actor = WeakActor.Get(); Actor->DoSomething(); } // GC๊ฐ€ SomeActor๋ฅผ ์ˆ˜์ง‘ํ•œ ํ›„ check(!WeakActor.IsValid()); // true - ์ž๋™์œผ๋กœ ๋ฌดํšจํ™” check(WeakActor.Get() == nullptr); // true // UPROPERTY๋กœ ์„ ์–ธ ์‹œ์—๋„ GC ๋ณดํ˜ธ ์•ˆ ํ•จ UPROPERTY() TWeakObjectPtr<AActor> WeakRef; // ์•ฝํ•œ ์ฐธ์กฐ ์œ ์ง€
TWeakObjectPtr์˜ UPROPERTY ๋™์ž‘

UPROPERTY๋กœ ์„ ์–ธ๋œ TWeakObjectPtr๋Š” GC ํ† ํฐ ์ŠคํŠธ๋ฆผ์—์„œ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, UPROPERTY()๊ฐ€ ์žˆ์–ด๋„ ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ GC๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” UObject*์™€ ๊ฒฐ์ •์ ์œผ๋กœ ๋‹ค๋ฅธ ์ ์ž…๋‹ˆ๋‹ค.

SECTION 03

TSoftObjectPtr - ์†Œํ”„ํŠธ ์ฐธ์กฐ

์—์…‹ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜์˜ ์ง€์—ฐ ๋กœ๋”ฉ ์ฐธ์กฐ

TSoftObjectPtr<T>

TSoftObjectPtr์€ ์—์…‹์˜ ๊ฒฝ๋กœ(FSoftObjectPath)๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์—์…‹์ด ๋ฉ”๋ชจ๋ฆฌ์— ์—†์–ด๋„ ๊ฒฝ๋กœ๋Š” ์œ ํšจํ•˜๋ฉฐ, ํ•„์š” ์‹œ ๋ช…์‹œ์ ์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. GC ๊ด€์ ์—์„œ ๋Œ€์ƒ ์—์…‹์„ ๋ณดํ˜ธํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

C++ UCLASS() class UInventoryItem : public UObject { GENERATED_BODY() // ์•„์ด์ฝ˜์€ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋กœ๋“œ UPROPERTY(EditAnywhere) TSoftObjectPtr<UTexture2D> Icon; // ๋ฉ”์‹œ๋„ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋กœ๋“œ UPROPERTY(EditAnywhere) TSoftObjectPtr<UStaticMesh> Mesh; UTexture2D* GetIcon() { // ๋™๊ธฐ ๋กœ๋“œ (๋กœ๋“œ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ๋กœ๋“œ) return Icon.LoadSynchronous(); } void LoadMeshAsync() { // ๋น„๋™๊ธฐ ๋กœ๋“œ FStreamableManager& SM = UAssetManager::GetStreamableManager(); SM.RequestAsyncLoad( Mesh.ToSoftObjectPath(), FStreamableDelegate::CreateUObject( this, &UInventoryItem::OnMeshLoaded) ); } };

ํฌ์ธํ„ฐ ํƒ€์ž…๋ณ„ GC ๋™์ž‘ ๋น„๊ต

ํƒ€์ž…GC ๋ณดํ˜ธ์—์…‹ ๋กœ๋“œnull ์•ˆ์ „์‚ฌ์šฉ ์‹œ์ 
TObjectPtr<T>๊ฐ•ํ•œ์ฆ‰์‹œGarbage ์ž๋™ ์ฒ˜๋ฆฌ๊ธฐ๋ณธ ์ฐธ์กฐ
UObject*๊ฐ•ํ•œ์ฆ‰์‹œ์ˆ˜๋™ ํ™•์ธ๋ ˆ๊ฑฐ์‹œ/๋กœ์ปฌ
TWeakObjectPtr์—†์Œ์ฆ‰์‹œIsValid() ์ฒดํฌ์บ์‹œ, ๊ด€์ฐฐ
TSoftObjectPtr์—†์Œ์ง€์—ฐ๊ฒฝ๋กœ๋งŒ ์œ ํšจ์—์…‹ ์ฐธ์กฐ
TSoftClassPtr์—†์Œ์ง€์—ฐ๊ฒฝ๋กœ๋งŒ ์œ ํšจํด๋ž˜์Šค ์ฐธ์กฐ
SECTION 04

Non-UObject ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ

TSharedPtr, TUniquePtr๋Š” GC์™€ ๋ฌด๊ด€ํ•œ ๋ณ„๋„ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

GC ์™ธ๋ถ€์˜ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ

UE5์—๋Š” GC์™€ ๋ฌด๊ด€ํ•œ C++ ์Šคํƒ€์ผ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€ Non-UObject์— ์‚ฌ์šฉ๋˜๋ฉฐ, ์ฐธ์กฐ ์นด์šดํŒ…์ด๋‚˜ ์œ ๋‹ˆํฌ ์†Œ์œ ๊ถŒ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

C++ // TSharedPtr - ์ฐธ์กฐ ์นด์šดํŒ… (Non-UObject ์ „์šฉ) TSharedPtr<FMyData> SharedData = MakeShared<FMyData>(); // ์ฐธ์กฐ ์นด์šดํŠธ๊ฐ€ 0์ด ๋˜๋ฉด ์ž๋™ ํ•ด์ œ // UObject์— ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋จ! (์ด์ค‘ ํ•ด์ œ ์œ„ํ—˜) // TWeakPtr - TSharedPtr์˜ ์•ฝํ•œ ๋ฒ„์ „ TWeakPtr<FMyData> WeakData = SharedData; // TUniquePtr - ์œ ์ผํ•œ ์†Œ์œ ๊ถŒ TUniquePtr<FMyData> UniqueData = MakeUnique<FMyData>(); // ์†Œ์œ ์ž๊ฐ€ ํŒŒ๊ดด๋˜๋ฉด ์ž๋™ ํ•ด์ œ // ์ž˜๋ชป๋œ ์‚ฌ์šฉ: UObject์— TSharedPtr ์‚ฌ์šฉ // TSharedPtr<UMyObject> Bad = ...; // ์ ˆ๋Œ€ ํ•˜์ง€ ๋งˆ์„ธ์š”! // GC์™€ ์ฐธ์กฐ ์นด์šดํŒ…์ด ์ถฉ๋Œํ•˜์—ฌ ํฌ๋ž˜์‹œ ๋ฐœ์ƒ
UObject์— TSharedPtr์„ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ 

UObject์˜ ์ˆ˜๋ช…์€ GC๊ฐ€ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. TSharedPtr์˜ ์ฐธ์กฐ ์นด์šดํŒ…๊ณผ GC์˜ ๋„๋‹ฌ ๊ฐ€๋Šฅ์„ฑ ๋ถ„์„์ด ์ถฉ๋Œํ•˜์—ฌ ์ด์ค‘ ํ•ด์ œ(double free)๋‚˜ ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. UObject์—๋Š” ๋ฐ˜๋“œ์‹œ TObjectPtr, TWeakObjectPtr, TSoftObjectPtr๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

SUMMARY

ํ•ต์‹ฌ ์š”์•ฝ

์ด ๊ฐ•์˜์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ
  • TObjectPtr๋Š” UE5์˜ ์ƒˆ ๊ธฐ๋ณธ ํฌ์ธํ„ฐ๋กœ, ์“ฐ๊ธฐ ๋ฐฐ๋ฆฌ์–ด์™€ MarkAsGarbage ์ž๋™ ์ฒ˜๋ฆฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค
  • TWeakObjectPtr๋Š” GC๋ฅผ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋Š” ์•ฝํ•œ ์ฐธ์กฐ๋กœ, ์บ์‹œ๋‚˜ ๊ด€์ฐฐ ํŒจํ„ด์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค
  • TSoftObjectPtr๋Š” ์—์…‹ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค
  • TSharedPtr/TUniquePtr๋Š” Non-UObject ์ „์šฉ์ด๋ฉฐ, UObject์— ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค
  • ์˜ฌ๋ฐ”๋ฅธ ํฌ์ธํ„ฐ ์„ ํƒ์ด GC ํšจ์œจ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ์„ ๋ชจ๋‘ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค
PRACTICE

๋„์ „ ๊ณผ์ œ

๋ฐฐ์šด ๋‚ด์šฉ์„ ์ง์ ‘ ์‹ค์Šตํ•ด๋ณด์„ธ์š”

์‹ค์Šต 1: TSharedPtr/TWeakPtr ์ˆ˜๋ช… ์ฃผ๊ธฐ ์‹คํ—˜

TSharedPtr๊ณผ TWeakPtr๋กœ ์ผ๋ฐ˜ C++ ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. ์ฐธ์กฐ ์นด์šดํŠธ ๋ณ€ํ™”๋ฅผ ๋กœ๊ทธ๋กœ ์ถ”์ ํ•˜๊ณ , TWeakPtr::IsValid()๊ฐ€ false๊ฐ€ ๋˜๋Š” ์‹œ์ ์„ ํ™•์ธํ•˜์„ธ์š”. UObject์—๋Š” TSharedPtr์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ ๋ฅผ ์ฝ”๋“œ๋กœ ์ฆ๋ช…ํ•˜์„ธ์š”.

์‹ค์Šต 2: TWeakObjectPtr ์•ˆ์ „์„ฑ ํ…Œ์ŠคํŠธ

TWeakObjectPtr๋กœ AActor๋ฅผ ์ฐธ์กฐํ•˜๊ณ , Actor๋ฅผ Destroyํ•œ ํ›„ TWeakObjectPtr::IsValid()์™€ TWeakObjectPtr::Get()์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ํ™•์ธํ•˜์„ธ์š”. GC ์ „ํ›„์˜ ๋™์ž‘ ์ฐจ์ด์™€ raw pointer ๋Œ€๋น„ ์•ˆ์ „์„ฑ์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

์‹ฌํ™” ๊ณผ์ œ: ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ ์„ฑ๋Šฅ ๋ฒค์น˜๋งˆํฌ

TObjectPtr, TWeakObjectPtr, TSoftObjectPtr, raw pointer์˜ ์ ‘๊ทผ ์†๋„๋ฅผ 100,000ํšŒ ๋ฐ˜๋ณต ์ ‘๊ทผ์œผ๋กœ ๋ฒค์น˜๋งˆํฌํ•˜์„ธ์š”. FPlatformTime::Seconds()๋กœ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ณ , ๊ฐ ํฌ์ธํ„ฐ ํƒ€์ž…์˜ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ •๋Ÿ‰์ ์œผ๋กœ ๋น„๊ตํ•˜์„ธ์š”.