【问题标题】:delphi lose value when freeing objectdelphi在释放对象时失去价值
【发布时间】:2015-04-20 08:39:14
【问题描述】:

对不起,如果我有同样的问题。

在 Delphi 中,我制作了这样的函数:

function TModuleDatabase.LoadCountryList():TDictionary<integer, String>;
var
  UQ: TUniQuery;
  UC: TUniConnection;
  CountryList: TDictionary<integer, String>;
begin
  CountryList := TDictionary<integer, String>.Create;
  UC := UniConnection2;
  UQ := TUniQuery.Create(nil);
  try
    UQ.Connection := UC;
    try
      UQ.SQL.Clear;
      UQ.SQL.Add('SELECT ID,NAME FROM COUNTRY ORDER BY NAME ASC');
      UQ.Open;
      while not UQ.Eof do
      begin
        CountryList.Add(UQ.Fields.FieldByName('ID').AsInteger,UQ.Fields.FieldByName('NAME').AsString);
        UQ.Next;
      end;
      Result := CountryList;
    except
      on E:Exception do
        ModuleMsgDialog.WarningMsg(E.Message);
    end;
  finally
    UQ.Close;
    UQ.Free;
    CountryList.Free;
  end;
end;

我将功能与其他 DataModule 分开,以使我不会在每个表单中每次都重复此功能。但是当我从表单中调用这个函数时:

procedure TCompanyDetailsForm.FormCreate(Sender: TObject);
var
  i: Integer;
  sItem: String;
  CountryList: TDictionary<integer, String>;
begin
  PageControl1.ActivePage := AddressTab;

  CountryList := ModuleDatabase.LoadCountryList();
  for i in CountryList.Keys do
  begin
    LocationCbbx.Items.AddObject(CountryList.Items[i],TObject(i));
  end;
end;

问题出在 CountryList.Free;。字典中的所有项目在使用前都已释放。 如果我不做free,就会造成内存泄漏。

如何在免费之前传输数据的最佳方式。或者如何在调用后以其他形式或单位释放价值。

感谢您的帮助。

【问题讨论】:

  • 你应该使用类似procedure TModuleDatabase.LoadCountryList( countries : TDictionary&lt;integer, String&gt; );的东西。这样,调用代码将(必须)提供字典的实例,调用者也将负责在需要时释放它。
  • 您可以可能将 TDictionary 设为 TModuleDatabase 的字段,并且您的所有表单都可以共享同一个实例,而不是每个都有自己的副本。

标签: delphi delphi-xe2


【解决方案1】:

您有两个主要选择。

选项 1 – 调用者提供实例化对象

在这里你让调用者承担终身责任。调用者传入一个实例化的对象,被调用者填充它。

procedure PopulateCountryDict(Countries: TDictionary<Integer, string>);
begin
  // populate Countries here
end;

选项 2 – 调用者返回一个新实例化的对象,该对象也已填充

这是可行的,但是一旦被调用者返回,调用者必须承担生命周期的责任。它看起来像这样:

function CreateAndPopulateCountryDict: TDictionary<Integer, string>;
begin
  Result := TDictionary<Integer, string>.Create;
  try
    // populate Result here
  except
    Result.Free; // until this function returns, we are responsible for lifetime
    raise;
  end;
end;

调用代码如下:

var
  Countries: TDictionary<Integer, string>
....
Countries := CreateAndPopulateCountryDict;
try
  // do stuff with Countries
finally
  Countries.Free;
end;

【讨论】:

  • 是的..是的..我不这么认为。我想我会选择选项 1,因为它更安全且足以完成我的需要。选项 2 让我必须更加小心 try 子句。感谢大卫的快速回复。
【解决方案2】:

作为大卫回答的扩展,还有另一个使用回调的选项

procedure LoadCountryList( ACallback : TProc<TDictionary<integer,string>> );
var
  LCountryList : TDictionary<integer,string>;
begin
  // create the instance
  LCountryList := TDictionary<integer,string>.Create;
  try
    // fill the dictionary

    // execute the callback
    ACallback( LCountryList );
  finally
    // free the instance
    LCountryList.Free;
  end;
end;

然后在你的代码中使用它

procedure TCompanyDetailsForm.FormCreate(Sender: TObject);
begin
  PageControl1.ActivePage := AddressTab;

  LoadCountryList( 
    procedure ( CountryList : TDictionary<integer,string> ) 
    var
      i: Integer;
    begin
      for i in CountryList.Keys do
      begin
        LocationCbbx.Items.AddObject(CountryList.Items[i],TObject(i));
      end;
    end );
end;

【讨论】:

  • 在这里为我提供更高级的解决方案。去罗马的方法很多。我认为这个答案对我的问题来说太过分了。 :D 但这也非常有用。感谢您的帮助。
【解决方案3】:

你应该在 FormCreate 方法中创建字典,并销毁或清除你需要的地方。不在 LoadCountryList 函数中。

【讨论】:

  • 是的,我同意你的看法。大卫先生的回复也用样本清楚地回答了我的问题。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多