UObject と FTickableGameObject を多重継承したクラスのクラッシュについて

UObject と FTickableGameObject を多重継承したクラスにおいて、

GC実行のタイミングでクラッシュが発生しました。

FTickableGameObject のドキュメントのNoteを参考に、

以下の対応を行うことでクラッシュは解消されています。

UTickableObject::UTickableObject()
  : FTickableGameObject(ETickableTickType::Never) // ① 初期化リストで Tick 登録を Never に設定
{
}
 
void UTickableObject::Initialize()
{
  // ② ゲームスレッド側から呼び出し、Tick を有効化
  SetTickableTickType(ETickableTickType::Conditional);
}
 
void UTickableObject::BeginDestroy()
{
  // ③ 破棄前に Tick を無効化
  SetTickableTickType(ETickableTickType::Never);
  Super::BeginDestroy();
}

この対応方針で問題ないか確認したいのですが、現状 Initialize() を都度呼び出す手間が発生しているため、

より簡潔な改善案があればご教示ください。

また、同様のパターンを複数のクラスで使用しており対応要否を精査したいため、

問題が発生する条件や対応が不要なケースについても合わせて教えていただけますか。

[Attachment Removed]

再現手順[Attachment Removed]

お世話になっております。

ご対応いただいた方針で問題ございません。Tickable.hのFTickableGameObjectクラスコメントに記載された推奨手順そのもので、エンジン内部でもUTickableWorldSubsystemが同じ構成を採用しております。

Initialize()の簡略化についてですが、全生成経路で「初期化完了後」かつ「ゲームスレッド上」を保証する自動フックは存在しないため、明示的なコードが前提となります。Worldに紐づくオブジェクトであればUTickableWorldSubsystemの継承が最も簡潔です。そうでない場合は共通の基底クラスを用意し、PostInitProperties()とPostLoad()から有効化する形にまとめてください。

発生条件の主因は、BeginDestroy()と実際のデストラクタの間のウィンドウです。登録解除はデストラクタで行われますが、インクリメンタルGCにより数フレーム後になることがあり、その間も破棄処理中のオブジェクトがTickされ続けます。ほかに、ゲームスレッド外での構築、GCワーカースレッドでの破棄、CDOの登録が要因となります。

精査の基準としては、UObject派生かつFTickableGameObjectを継承しているクラスはすべて対応が必要とお考えください。再現していないものもGCタイミング依存の潜在不具合となります。対応不要なのは、UObject派生でない場合、UTickableWorldSubsystemなど対応済みのエンジンクラスを継承している場合、Tickを有効化していない場合です。

引き続きよろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご回答いただきありがとうございます。

方針のご確認と詳細な発生条件・精査基準をご共有いただき、大変参考になりました。

いただいた内容をもとに、対象クラスの洗い出しと対応を進めてまいります。

ご対応いただきありがとうございました。

[Attachment Removed]