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

Delphi 10.2内置JSON库销毁根对象出错,求优雅解决方案

Delphi 10.2 内置JSON库重复引用对象销毁错误的优雅解决方法

你的代码报错核心原因是同一JSON子对象被多次添加到不同父容器时,默认会被多个父容器同时声明拥有所有权。当销毁根对象aJsonObj时,会先释放aJsonArr,而aJsonArr会释放aJsonObj2和aJsonObj3;随后aJsonObj尝试释放自己a、b键对应的aJsonObj2、aJsonObj3,这就导致了重复释放已回收内存的错误。

不需要自行编写递归销毁的DestroyJSONObject过程,利用Delphi内置JSON库自带的功能就能优雅解决问题,以下是具体方案:

解决方案1:控制父容器的所有权(推荐)

Delphi的TJSONObject.AddPair和TJSONArray.Add都支持指定是否接管子对象的所有权(默认是True)。对于需要重复引用的子对象,只让其中一个父容器拥有所有权,其他容器设置AOwnsValue=False即可:

修改后的代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  aJsonObj,aJsonObj2,aJsonObj3 : TJsonObject;
  aJsonArr : TJsonArray;
begin
  aJsonObj := TJsonObject.Create;
  aJsonObj2 := TJsonObject.Create;
  aJsonObj3 := TJsonObject.Create;
  aJsonArr := TJsonArray.Create;
  aJsonObj2.AddPair('aa','aa');
  aJsonObj3.AddPair('bbb','bbb');
  aJsonArr.Add(aJsonObj2); // 让数组拥有aJsonObj2、aJsonObj3的所有权
  aJsonArr.Add(aJsonObj3);
  aJsonObj.AddPair('arr',aJsonArr);
  // 重复添加时设置不接管所有权
  aJsonObj.AddPair('a',aJsonObj2, False);
  aJsonObj.AddPair('b',aJsonObj3, False);
  ShowMessage(aJsonObj.ToJSON);
  FreeAndNil(aJsonObj); // 释放根对象时,数组会释放aJsonObj2、aJsonObj3,不会重复释放
end;

解决方案2:克隆对象避免共享实例

如果业务上不需要共享同一个JSON对象实例,而是需要相同内容的独立副本,可以使用TJSONValue.Clone方法复制对象,让每个父容器拥有自己的副本:

修改后的代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  aJsonObj,aJsonObj2,aJsonObj3 : TJsonObject;
  aJsonArr : TJsonArray;
begin
  aJsonObj := TJsonObject.Create;
  aJsonObj2 := TJsonObject.Create;
  aJsonObj3 := TJsonObject.Create;
  aJsonArr := TJsonArray.Create;
  aJsonObj2.AddPair('aa','aa');
  aJsonObj3.AddPair('bbb','bbb');
  aJsonArr.Add(aJsonObj2); // 数组拥有原对象
  aJsonArr.Add(aJsonObj3);
  aJsonObj.AddPair('arr',aJsonArr);
  // 添加克隆后的副本,根对象拥有副本的所有权
  aJsonObj.AddPair('a',aJsonObj2.Clone as TJSONObject);
  aJsonObj.AddPair('b',aJsonObj3.Clone as TJSONObject);
  ShowMessage(aJsonObj.ToJSON);
  FreeAndNil(aJsonObj); // 根对象释放副本,数组释放原对象,无冲突
end;

不推荐的方案:手动管理所有对象生命周期

如果一定要自己控制所有对象的释放,可以在所有添加操作中都设置AOwnsValue=False,最后手动释放所有创建的对象。但这种方式容易遗漏对象导致内存泄漏,仅适合特殊场景:

procedure TForm1.Button1Click(Sender: TObject);
var
  aJsonObj,aJsonObj2,aJsonObj3 : TJsonObject;
  aJsonArr : TJsonArray;
begin
  aJsonObj := TJsonObject.Create;
  aJsonObj2 := TJsonObject.Create;
  aJsonObj3 := TJsonObject.Create;
  aJsonArr := TJsonArray.Create;
  aJsonObj2.AddPair('aa','aa');
  aJsonObj3.AddPair('bbb','bbb');
  aJsonArr.Add(aJsonObj2, False);
  aJsonArr.Add(aJsonObj3, False);
  aJsonObj.AddPair('arr',aJsonArr, False);
  aJsonObj.AddPair('a',aJsonObj2, False);
  aJsonObj.AddPair('b',aJsonObj3, False);
  ShowMessage(aJsonObj.ToJSON);
  // 手动释放所有对象
  FreeAndNil(aJsonObj);
  FreeAndNil(aJsonArr);
  FreeAndNil(aJsonObj2);
  FreeAndNil(aJsonObj3);
end;

总结

优先推荐第一种方案(控制所有权),它既能保留对象的共享引用,又能避免重复释放的问题;如果需要独立的对象实例,则选择克隆方案。

内容的提问来源于stack exchange,提问作者Serkan Ekşioğlu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:02:07