#1. 概要
UE4のGCについて基本的なことを纏めました。
GCの軽量化等については触れません。
環境
・UE4.26
・Visual Studio2019
#2. UObjectの生成
GCについて解説する前にUObjectの生成方法について軽く触れておきます。
C++側でUObjectを生成するには基本的にNewObjectを使用します(他にも生成方法はあります)。
C++のnewは使用しません。
auto hoge = NewObject<UHoge>(GetWorld());
後述するGCの対象から外したい場合、NewObjectのフラグにRF_MarkAsRootsetを指定することができます。
auto hoge = NewObject<AActor>(GetWorld(), NAME_None, RF_MarkAsRootSet);
また、UObjectを継承したクラスのメンバ変数は、明示的に初期化をしなくてもゼロ初期化されるという特徴があります。
[UObject インスタンスの作成 - Unreal Engine]
(https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Objects/Creation/#newobject)
##2.1. NewNamedObjectとConstructObjectについて
UE4のドキュメントにはフラグを指定する関数としてNewNamedObjectの紹介がされていますが、この関数本当に存在するのでしょうか?
NewObjectのオーバーロードに置き換えられているような気がします。
自分の探し方が悪いのかもしれませんがUObjectGlobal.hには定義が見当たりませんでした。
同様にConstructObjectも見当たりませんでした。
#3. UE4のGC(ガベージコレクション)
UE4は参照されなくなった、または破棄されたUObjectを自動的に破棄するGC(ガベージコレクション)を搭載しています。
GCは破棄したUObjectを参照するすべてのUPROPERTYとコンテナ(TArray、TMapなど)内のUActorComponentとAActor参照にnullptrを代入します。
これによって破棄された領域へのアクセスやダングリングポインタを防いでくれます。
GCの対象から外すにはUPROPETYを使用する必要があります(GCが機能しなくなるわけではなく、UPROPERTYを付けると参照中であることをGCに伝えている)。
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UHogeComponent* Hoge;
必ずしもすべてのUObjectはUPROPERTYである必要はありませんが、その場合扱いには気を付ける必要があります。
UPROPERTYではないUObjectに対する安全なアクセス方法としてスマートポインタであるTWeakObjectPtrが用意されています。
[アンリアルでのオブジェクト処理 - Unreal Engine]
(https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Objects/Optimizations/)
また、UPROPERTYを使用できない場合以下のような方法もあるみたいです。
[UPROPERTY を使わずにオブジェクトをガーベジ コレクションの対象から外す方法 - Qiita]
(https://qiita.com/go_astrayer/items/11b99ef8849b43796fee)
##3.2. UE4.25までのUProperty
UE4にはFPropertyと呼ばれる、プロパティシステムによって作られたC++クラスのメンバー変数や関数の引数の情報を持つクラスがあります。
UE4.25まではこれはUPropertyと呼ばれており、UObjectを継承していました。
UObjectを継承しているためGCの対象となり、問題となっていたようです。
ですが、UE4.25からはUObjectを継承しなくなりGCの対象とならなくなりました。
[UProperty -> FPropertyへの変化(UE4 4.25) - Hatena Blog]
(https://www.ayumax.net/entry/2020/03/22/144226)
#4. UObjectの破棄とIsValid関数
UObjectを明示的に破棄するにはUObject::ConditionalBeginDestroyを呼び出す必要があります。
しかしこの関数を使用することはあまりないでしょう。
Hoge->ConditionalBeginDestroy();
UObjectを継承しているAActor、UActorComponentを破棄するにはAActor::Destroy、UActorComponent::DestroyComponentを呼び出します。
これによってGCの破棄対象となります。
HogeActor->Destroy();
HogeComponent->DestroyComponent();
UObjectが有効なオブジェクト(null or GCの破棄対象)かどうかになっているかどうかを判定するにはIsValidを呼び出します。
IsValid(Hoge);
#5. UE4のスマートポインタ
UObjectを継承していないクラスはGCの対象になりません。そのようなオブジェクトを管理するためにスマートポインタが用意されています。
- TUniquePtr
- TSharedPtr
- TSharedRef
- TWeakPtr
- TWeakObjectPtr
TWeakObjcetPtrはUObjectに対する弱参照を持ちます。それ以外のスマートポインタをUObjectに使用しないでください。
auto Hoge = NewObject<UHoge>(GetWorld());
TWeakObjectPtr<UObject> WHoge = Hoge;
WHoge->HogeHoge(); // 参照
if(WHoge.IsValid()) WHoge->HogeHoge(); // 有効かどうか確かめてから参照
if(WHoge.Get(true)) WHoge->HogeHoge(); // 有効かどうか確かめてから参照(MarkPendingKillに関わらず)
if(WHoge.Get(false)) WHoge->HogeHoge(); // 有効かどうか確かめてから参照(MarkPendingKillの場合null)
[Unreal スマート ポインタ ライブラリ - Unreal Engine]
(https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/SmartPointerLibrary/)