delphi在释放对象时丢失值

delphi在释放对象时丢失值,delphi,delphi-xe2,Delphi,Delphi Xe2,对不起,如果我的问题也一样 在Delphi中,我生成如下函数: function TModuleDatabase.LoadCountryList():TDictionary<integer, String>; var UQ: TUniQuery; UC: TUniConnection; CountryList: TDictionary<integer, String>; begin CountryList := TDictionary<integer

对不起,如果我的问题也一样

在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;
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;
函数TModuleDatabase.LoadCountryList():t字典;
变量
UQ:TUniQuery;
UC:连接;
国家列表:t词典;
开始
CountryList:=TDictionary.Create;
UC:=单连接2;
UQ:=TUniQuery.Create(nil);
尝试
UQ.连接:=UC;
尝试
UQ.SQL.Clear;
Add('SELECT ID,NAME FROM COUNTRY ORDER BY NAME ASC');
开放式;
而不是UQ.Eof do
开始
CountryList.Add(UQ.Fields.FieldByName('ID').AsInteger,UQ.Fields.FieldByName('NAME').AsString);
UQ.Next;
结束;
结果:=国家列表;
除了
关于E:Exception-do
ModuleMsgDialog.WarningMsg(E.Message);
结束;
最后
UQ.关闭;
UQ.免费;
国家名单。免费;
结束;
结束;
我将该函数与其他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;
过程tcompanydailsform.FormCreate(发送方:TObject);
变量
i:整数;
sItem:字符串;
国家列表:t词典;
开始
PageControl1.ActivePage:=地址选项卡;
CountryList:=ModuleDatabase.LoadCountryList();
因为我在CountryList中。按键可以
开始
LocationCbbx.Items.AddObject(CountryList.Items[i],TObject(i));
结束;
结束;
问题出在CountryList.Free。字典中的所有项在使用前已释放。 如果我不释放内存,就会导致内存泄漏

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


谢谢您的帮助。

您有两个主要选择

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

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

procedure PopulateCountryDict(Countries: TDictionary<Integer, string>);
begin
  // populate Countries here
end;
procedure PopulateCountryDict(国家/地区:t词典);
开始
//在这里居住的国家
结束;
选项2–调用者返回一个新实例化的对象,该对象也被填充

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

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;
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;
函数CreateAndPopulateCountryDict:TDictionary;
开始
结果:=t字典。创建;
尝试
//在此处填充结果
除了
Result.Free;//在该函数返回之前,我们负责整个生命周期
提高;
结束;
结束;
调用代码如下所示:

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;
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;
var
国家:t词典
....
国家:=CreateAndPopulateCountryDict;
尝试
//与国家打交道
最后
国家。免费;
结束;

您应该在FormCreate方法中创建词典,并销毁或清除您需要的位置。不在LoadCountryList函数中

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

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-LoadCountryList(ACallback:TProc);
变量
LCountryList:TDictionary;
开始
//创建实例
LCountryList:=TDictionary.Create;
尝试
//查字典
//执行回调
ACallback(LContrylist);
最后
//释放实例
自由的;
结束;
结束;
然后在代码中使用它

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;
过程tcompanydailsform.FormCreate(发送方:TObject);
开始
PageControl1.ActivePage:=地址选项卡;
加载国家列表(
程序(国家列表:t词典)
变量
i:整数;
开始
因为我在CountryList中。按键可以
开始
LocationCbbx.Items.AddObject(CountryList.Items[i],TObject(i));
结束;
(完),;
结束;

您应该使用类似于
过程TModuleDatabase.LoadCountryList(国家/地区:TDictionary)。这样,调用代码将(必须)提供字典的一个实例,调用方还将负责在需要时释放字典。您可能会将TDictionary设置为TModuleDatabase的一个字段,并且您的所有表单都可以共享同一个实例,而不是各自拥有副本。是的。。对我不这么认为。我想我会选择选项1,因为它更安全,足以满足我的需要。选项2使我不得不更加小心地使用try子句。谢谢大卫的快速回复。是的,我同意你的看法。大卫先生的回答也很清楚地回答了我的问题。谢谢你的帮助。这里有更高级的解决方案。去罗马有很多方法。我认为这个答案对我的问题来说太过分了但这也很有用。谢谢你的帮助。