🐡

【UE5】TMapに対してValueをObjectPtrを型にした状態でAdd関数を呼ばずに=で代入させるとハングしたしたときの解決法

2024/11/02に公開

概要

以下のようなコードを書いた時にハングしたので、短いですが備忘録も兼ねて記事にしました。

AActor* ExampleActor;
TMap<uint32_t,ObjectPtr<AActor>> Example;
Example[0] = ExampleActor; //ハング

環境

UE5.4.4

原因

何らかの方法で先ほどのコードを実行させるようにすると、以下の場所で止まりました。

FObjectPtr& operator=(UObject* Other)
{
#if UE_OBJECT_PTR_GC_BARRIER
    ConditionallyMarkAsReachable(Other); //この中で止まった
#endif // UE_OBJECT_PTR_GC_BARRIER
    Handle = UE::CoreUObject::Private::MakeObjectHandle(Other);
    return *this;
}
ObjectPtr.h
FORCEINLINE void ConditionallyMarkAsReachable(const UObject* InObj) const
{
    if (UE::GC::GIsIncrementalReachabilityPending && InObj)
    {
        UE::GC::MarkAsReachable(InObj);
    }
}

どうやらGC周りでバリアを張られているみたいということがわかりました。

対策

これを解決する方法は=をやめて最初はAdd関数で要素を入れることでした。

AActor* ExampleActor;
TMap<uint32_t,ObjectPtr<AActor>> Example;
Example.Add(0,ExampleActor); //ハングしなくなった

考えてみれば当たり前な解決方法かもしれないですが、誤って書く可能性も考えられるので、ハマった方の参考になれば幸いです。

Add関数を入れた以降は、その要素に対してで代入しても大丈夫でした。

AActor* ExampleActor;
TMap<uint32_t,ObjectPtr<AActor>> Example;
Example.Add(0,ExampleActor); //ハングしなくなった
Example[0] = ExampleActor;   //既に要素を確保してるからかハングしないし
Example[0] = nullptr;        //nullptrを入れてもハングしないし、
Example[0] = ExampleActor;   //その状態で再代入してもハングしない

余談

気になったのでAdd関数の中身を見てみました。

Map.h
FORCEINLINE ValueType& Add(		 KeyType&& InKey,		ValueType&& InValue) { return Emplace(MoveTempIfPossible(InKey), MoveTempIfPossible(InValue)); }
Map.h
template <typename T>
UE_INTRINSIC_CAST FORCEINLINE std::remove_reference_t<T>&& MoveTempIfPossible(T&& Obj)
{
    using CastType = std::remove_reference_t<T>;
    return (CastType&&)Obj;
}

このstd::remove_reference_tは何かというと、型から参照を除去した状態で返してくれる機能らしい
つまり参照情報を外すことで、GCのバリアから防がれずに通れる様になったという自分の中で解釈しました。

参考
std::remove_reference

Discussion