You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unreal C++中如何防止循环内的Object对象被意外销毁?

Unreal C++ GeoJSON解析对象存活问题修复方案

问题根因

你遇到的parent address is invalid错误,本质是FJsonObject共享指针的引用计数提前归零导致对象被回收,核心原因有三个:

  1. 默认FJsonSerializer反序列化生成的FJsonObject,其生命周期绑定在父级FJsonValue(也就是RawJsonValueArray里的元素)上,如果后续操作意外修改了父级数组内容,或者共享指针的引用被提前释放,子对象就会被销毁
  2. 当前代码没有做非空校验,如果对应索引的FJsonValue不是Object类型、或者目标字段不存在,GetObjectField/GetStringField会触发不可预期的内存访问错误
  3. 标准GeoJSON的顶层结构通常是Object而不是Array,如果反序列化的目标和实际Json结构不匹配,也会导致解析出来的对象结构异常。

修复方案

按照以下步骤调整代码即可保证GeoJsonObject的存活状态:

  • 把GeoJsonObject的声明移到循环内部,避免跨循环迭代的指针复用导致的异常
  • 新增非空校验,确认所有指针有效后再访问字段
  • 如果需要在循环外长期持有解析结果,单独用数组存储FJsonObject的共享指针,主动增加引用计数
  • 如果你的GeoJSON顶层是单个Object,需要把反序列化的输出参数从数组改成单个FJsonObject共享指针

修正后的参考代码

void test::ParseGeoJson(const FString& File)
{
    FString JsonString;
    if (!FFileHelper::LoadFileToString(JsonString, *File))
    {
        UE_LOG(LogTemp, Error, TEXT("加载Json文件失败:%s"), *File);
        return;
    }

    // 如果确认你的GeoJSON顶层是数组,保留以下反序列化逻辑
    TArray<TSharedPtr<FJsonValue>> RawJsonValueArray;
    TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(*JsonString);
    bool success = FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
    
    // 如果你的GeoJSON顶层是Object,用下面的代码替换上面的反序列化逻辑
    // TSharedPtr<FJsonObject> RootObject;
    // TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(*JsonString);
    // bool success = FJsonSerializer::Deserialize(Reader, RootObject);
    
    if (success && RawJsonValueArray.Num() > 0)
    {
        // 如果你需要长期持有所有解析后的对象,定义这个数组来存,主动加引用计数
        TArray<TSharedPtr<FJsonObject>> AllParsedGeoObjects;
        
        for (int32 index = 0; index < RawJsonValueArray.Num(); index++)
        {
            // 先检查当前Json值是不是Object类型
            if (!RawJsonValueArray[index]->IsObject())
            {
                continue;
            }
            // 把声明移到循环内部
            TSharedPtr<FJsonObject> GeoJsonObject = RawJsonValueArray[index]->AsObject();
            if (!GeoJsonObject.IsValid())
            {
                continue;
            }
            
            // 读取字段前先判断字段是否存在
            TSharedPtr<FJsonObject> geometry = nullptr;
            if (GeoJsonObject->HasTypedField<EJson::Object>("geometry"))
            {
                geometry = GeoJsonObject->GetObjectField("geometry");
            }
            
            FString type;
            if (GeoJsonObject->HasTypedField<EJson::String>("type"))
            {
                type = GeoJsonObject->GetStringField("type");
            }

            // 其余字段读取也加同样的校验逻辑
            TSharedPtr<FJsonObject> properties = GeoJsonObject->HasTypedField<EJson::Object>("properties") ? GeoJsonObject->GetObjectField("properties") : nullptr;
            TSharedPtr<FJsonObject> layer = GeoJsonObject->HasTypedField<EJson::Object>("layer") ? GeoJsonObject->GetObjectField("layer") : nullptr;
            FString source = GeoJsonObject->HasTypedField<EJson::String>("source") ? GeoJsonObject->GetStringField("source") : TEXT("");

            // 如果需要长期持有,加入数组,引用计数+1,对象就不会被提前释放
            AllParsedGeoObjects.Add(GeoJsonObject);
        }

        // 后续可以正常使用AllParsedGeoObjects里的对象
    }
}

额外建议

如果不需要动态读取Json字段,建议解析完成后直接把需要的属性转成自定义结构体存储,不要长期持有FJsonObject指针,能彻底避免共享指针生命周期管理带来的野指针问题。

内容的提问来源于stack exchange,提问作者dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 12:06:03