1

尝试使用 TJSONObject (Delphi XE4) 解析 JSON 文件。

解析后我想销毁 TJSONObject 以防止内存泄漏,但是:

procedure TfmMain.ReadIngrJSON(const fName: string);
var i: integer;
    S: TStringList;
    JSONObject, innerObject: TJSONObject;
    innerArray: TJSONArray;
begin
S:=TStringList.Create;
try
  S.LoadFromFile(fName);
  JSONObject:=TJSONObject.ParseJSONValue(S.Text) as TJSONObject;
  if Assigned(JSONObject) then
    begin
      SetLength(ingrArray, JSONObject.Size);
      for i := 0 to JSONObject.Size-1 do
        begin
          ingrArray[i].id:=JSONObject.Get(i).JsonString.Value;
          innerObject:=JSONObject.Get(ingrArray[i].id).JsonValue as TJSONObject;

          innerArray:=innerObject.Get('en').JsonValue as TJSONArray;
          ingrArray[i].name[0]:=innerArray.Get(0).Value;
          ingrArray[i].units[0]:=innerArray.Get(1).Value;

          innerArray:=innerObject.Get('ru').JsonValue as TJSONArray;
          ingrArray[i].name[1]:=innerArray.Get(0).Value;
          ingrArray[i].units[1]:=innerArray.Get(1).Value;
          innerArray:=nil;
        end;
      innerObject.Destroy;

      for i := 0 to Length(ingrArray)-1 do
        listIngredients.Items.Add(ingrArray[i].name[1]);

    end
  else
    raise Exception.Create('no JSON data');
finally
  JSONObject.Destroy;  //here is an error 'invalid pointer operation'
  S.Free;
end;

end;

我的代码有什么问题?

4

2 回答 2

1

It is not your responsibility to free the reference returned by the call to Get. That responsibility lies with the object that you call Get on, in your code that is JSONObject. You must remove the line that calls innerObject.Destroy.

The reason you are encountering a invalid pointer operation error is that JSONObject is trying to destroy an object that you already destroyed.


You typically do not call Destroy in case the reference on which you call it happens to be nil. Instead call Free which performs the nil check and skips the call to Destroy if the reference is nil.

Finally, your use of finally is not correct. The correct pattern is:

obj := TSomeClass.Create;
try
  // use obj
finally
  obj.Free; // obj.Destroy is also fine in this case because obj <> nil
end;

You must put the try immediately after the constructor assigns to the reference. If you put it before, then your call to Free can act on an uninitialized reference. If you don't put it immediately after, then you may leak.

For your JSON object you'd write it like this:

JSONObject := TJSONObject.ParseJSONValue(S.Text) as TJSONObject;
if Assigned(JSONObject) then
  try
    ....
  finally
    JSONObject.Free;
  end;

Or equally you might do this:

JSONObject := TJSONObject.ParseJSONValue(S.Text) as TJSONObject;
try
  if Assigned(JSONObject) then
  begin
    ....
  end;
finally
  JSONObject.Free;
end;
于 2013-07-03T08:57:06.150 回答
1

正确的模式 - 甚至内置在 Delphi 代码模板中

Object-var := Object-Class.Create; // or any other way to create the instance
try
    // ...
finally
  Object-var.Destroy;
end;

相反,您将对象创建放在代码中间tryfinally这可以保证:

  • 如果在S.LoadFromFile(fName);此过程中发生某些异常,则会调用Destroy垃圾指针。

  • 如果在JSONObject:=TJSONObject.ParseJSONValue(S.Text) as TJSONObject;此过程中发生某些异常,则会调用Destroy垃圾指针。

  • 如果nil是 this 的结果将JSONObject:=TJSONObject.ParseJSONValue(S.Text) as TJSONObject;调用指针;Destroynil


总的来说,我对 DBX JSON 的了解很少——很多人抱怨错误或工作缓慢。而且你看起来也很难理解什么是对象生命周期以及如何在 Delphi 中管理它。出于这两个原因,我认为您最好使用经过多年测试的 refcounting-bases JSON 库而不是 DBX。

http://superobject.googlecode.com/git/readme.html

于 2013-07-03T08:54:46.497 回答