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

Flutter中如何解决多模型双向关联引发的实例化死循环问题

循环依赖实例化死循环问题

问题场景

开发旅游类应用时定义Place、PlaceInfo、County、Category四个数据模型:

  • County类包含List<Place?>类型的places属性,存储区县下关联的景点列表
  • Place类包含County类型的county属性,存储景点归属的区县信息
    实例化对象时触发无限递归:创建Place对象会调用County构造方法生成关联的区县实例,创建County实例时又会调用Place构造方法生成区县下的景点列表,循环调用无法退出,始终无法完成对象实例化。

原有模型代码

class Place {
  final String id;
  final String name;
  final String image;
  final List<County?> county;
  final List<PlaceInfo> placeInfos;
  final String firstInfo;
  final List<Category?> categories;

  Place({
    required this.id,
    required this.name,
    required this.image,
    required this.county,
    required this.placeInfos,
    required this.firstInfo,
    required this.categories,
  });

  factory Place.fromMap(Map<String, dynamic> place) => Place(
      id: place["id"],
      name: place["name"],
      image: place["image"],
      county: List.from(Repository().countiesJSON.map((countyJSON) {
        if (countyJSON.containsValue(place["countyId"])) {
          return County.fromMap(countyJSON);
        }
      }).toList()),

      placeInfos: List<PlaceInfo>.from(
        place["information"].map(
          (item) => PlaceInfo.fromMap(item),
        ),
      ),
      firstInfo: place["firstInfo"],
      categories: List.from(place["categoriesId"]
          .map(
            (categoyId) => Repository()
                .categoriesJSON
                .map((categoryJSON) {
                  if (categoryJSON.containsValue(categoyId)) {
                    return Category.fromMap(categoryJSON);
                  }
                })
                .iterator
                .current,
          )
          .toList()));
}

class PlaceInfo {
  final String infoTitle;
  final String info;

  PlaceInfo({
    required this.infoTitle,
    required this.info,
  });
  factory PlaceInfo.fromMap(Map<String, dynamic> placeInfo) => PlaceInfo(
        infoTitle: placeInfo["infoTile"],
        info: placeInfo["info"],
      );
}


class Category {
  String id;
  final String name;
  final String image;
  final List<Place?> places;

  Category({
    required this.id,
    required this.name,
    required this.image,
    required this.places,
  });

  factory Category.fromMap(Map<String, dynamic> category) => Category(
        id: category["id"],
        name: category["name"],
        image: category["image"],
        places: List.from(category["placesId"]
            .map(
              (placeId) => Repository()
                  .placesJSON
                  .map((placeJSON) {
                    if (placeJSON.containsValue(placeId)) {
                      return Place.fromMap(placeId);
                    }
                  })
                  .iterator
                  .current,
            )
            .toList()),
      );
}


class County {
  final String id;
  final String name;
  final String image;
  final List<Place?> places;

  County({
    required this.id,
    required this.name,
    required this.image,
    required this.places,
  });

  factory County.fromMap(Map<String, dynamic> county) => County(
        id: county["id"],
        name: county["name"],
        image: county["image"],
        places: List.from(county["placesId"]
            .map((placeId) => Repository().placesJSON.map((placeJSON) {
                  if (placeJSON.containsValue(placeId)) {
                    return Place.fromMap(placeJSON);
                  }
                }).toList())
            .toList()),
      );
}

测试演示数据

var placesData = [
  {
    "id": "d",
    "name": "Dolmabahçe Sarayı",
    "image": "assets/images/dolmabahçe.jpg",
    "countyId": "b",
    "firstInfo": "Dolmabahçe Sarayı firstInfo",
    "information": [
      {"infoTile": "bilgi 1", "info": "bilgi 1 açıklama"},
      {"infoTile": "bilgi 2", "info": "bilgi 2 açıklama"},
      {"infoTile": "bilgi 3", "info": "bilgi 3 açıklama"},
    ],
    "categoriesId": [
      "m",
      "s",
    ],
  },
  {
    "id": "a",
    "name": "Ayasofya",
    "image": "assets/images/ayasofya.webp",
    "countyId": "s",
    "firstInfo": "Ayasofya firstInfo",
    "information": [
      {"infoTile": "bilgi 1", "info": "bilgi 1 açıklama"},
      {"infoTile": "bilgi 2", "info": "bilgi 2 açıklama"},
      {"infoTile": "bilgi 3", "info": "bilgi 3 açıklama"},
    ],
    "categoriesId": [
      "c",
    ],
  },
  {
    "id": "y",
    "name": "Yerebatan Sarnıcı",
    "image": "assets/images/yerebatan.webp",
    "countyId": "s",
    "firstInfo": "Yerebatan Sarnıcı firstInfo",
    "information": [
      {"infoTile": "bilgi 1", "info": "bilgi 1 açıklama"},
      {"infoTile": "bilgi 2", "info": "bilgi 2 açıklama"},
      {"infoTile": "bilgi 3", "info": "bilgi 3 açıklama"},
    ],
    "categoriesId": [
      "m",
    ],
  },
];

var categoriesData = [
  {
    "id": "m",
    "name": "Müze",
    "image": "assets/images/müze.webp",
    "placesId": [
      "d",
      "y",
    ],
  },
  {
    "id": "r",
    "name": "Restorant",
    "image": "assets/images/restorant.webp",
    "placesId": [],
  },
  {
    "id": "e",
    "name": "Eğlence",
    "image": "assets/images/eğlence.webp",
    "placesId": [],
  },
  {
    "id": "s",
    "name": "Saray",
    "image": "assets/images/saray.webp",
    "placesId": [
      "d",
    ],
  },
  {
    "id": "a",
    "name": "Alışveriş",
    "image": "assets/images/alışveriş.webp",
    "placesId": [],
  },
  {
    "id": "c",
    "name": "Cami",
    "image": "assets/images/cami.webp",
    "placesId": [
      "a",
    ],
  },
  {
    "id": "d",
    "name": "Doğa",
    "image": "assets/images/doğa.webp",
    "placesId": [],
  },
];

var countiesData = [
  {
    "id": "b",
    "name": "Beşiktaş",
    "image": "assets/images/beşiktaş.jpg",
    "placesId": [
      "d",
    ],
  },
  {
    "id": "s",
    "name": "Sultanahmet",
    "image": "assets/images/Sultanahmet.jpg",
    "placesId": [
      "a",
      "y",
    ]
  }
];

问题根因

  • 双向关联模型的fromMap解析逻辑没有做实例缓存,每次遇到关联字段就递归新建关联对象,碰到循环引用就会无限嵌套调用
  • 字段定义不符合业务逻辑:Place类的county字段定义为List<County?>,实际业务中单个景点仅归属一个区县
  • 关联匹配逻辑存在错误:使用iterator.current遍历取值的写法无法正确获取匹配id的实例,多层列表嵌套转换逻辑冗余

修复方案

  • 调整模型字段定义:将Place类的county字段改为County?类型,匹配单归属的业务逻辑;将所有需要后续补全关联的字段设为可修改,支持分阶段赋值
  • 增加全局实例缓存:分别建立Place、County、Category的id到实例的缓存Map,同一个id的对象仅初始化一次,避免重复构造触发递归
  • 分两阶段完成数据解析:
    • 第一阶段:遍历所有原始JSON数据,仅解析各模型的基础非关联字段(id、名称、图片、景点详情、基础文本信息等),解析完成后存入对应缓存
    • 第二阶段:基于缓存的实例,批量补全所有关联关系:给景点实例设置归属区县、关联分类列表,给区县实例设置下辖景点列表,给分类实例设置关联景点列表
  • 替换错误的id匹配逻辑:使用firstWhere方法精准匹配对应id的缓存实例,移除冗余的列表嵌套转换

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:57:21