Delphi 如何管理为作为参数创建的对象分配的内存?

Art*_*ujo 1 delphi memory-management

标题可能看起来有点令人困惑,但我的问题的核心是:在以下两种情况下分配的内存会发生什么情况?

场景一:

var
 FHTTPResponse: IHTTPResponse;

...

FHTTPResponse := HTTPClient.Post(RequestURL, TStringStream.Create(APostParams.ToString, TEncoding.UTF8));
Run Code Online (Sandbox Code Playgroud)

TStringStream对象是在函数的参数列表中创建的(在本例中为THTTPClient.Post)。当我创建它时,分配给它的内存会发生什么,但(据我所知)我无法释放它。

场景2:

  ExampleClass = class
    private
      FName, FValue: string;
    public
      constructor Create(AName, AValue: string);

      function AsStringList: TStringList;
  end;

implementation

function ExampleClass.AsStringList: TStringList;
begin
  Result := TStringList.Create;
  Result.Add(FValue);
  Result.Add(FName);
end;

constructor ExampleClass.Create(AName, AValue: string);
begin
  FValue := AValue;
  FName := AName;
end;
Run Code Online (Sandbox Code Playgroud)

当用户调用时ExampleClass.AsStringList,我的理解是我不会返回对对象的引用,就像我有一个字段一样FAsStringList,而是返回对象本身

在这种情况下,我将传递释放对象的责任TStringList(这感觉是错误的)。在这个回报之下还有其他事情发生吗?是否有更好的方法将数据作为不同的结构返回,这并不意味着让用户有责任释放对象,而他们甚至不知道自己必须这样做?

TL;DR是否有更好(或正确)的方法来将数据表示形式返回为不同的对象类型,而不将内存管理职责转移给用户?

Rem*_*eau 10

Delphi 中的对象类型变量/参数/返回等是对对象的引用,而不是实际对象本身。对象仅驻留在动态内存中,不能按值传递,只能通过引用传递。

Create()对对象类型的任何调用都必须Free()有对(或)的匹配调用,Destroy()以从内存中释放对象,否则对象将被泄漏。

因此,在场景 1 中,TStringStream如果HTTPClient.Post()不取得它的所有权并释放它,则会泄漏。在场景 2 中,TStringList如果 的调用者AsStringList()没有获得返回列表的所有权并释放它,则 会泄漏。

在场景 1 中,我们假设Post()不拥有流的所有权(似乎是一个合理的假设)。调用者应该维护流的所有权并在Post()返回后释放它,例如:

var
  FHTTPResponse: IHTTPResponse;
  Strm: TStringStream;

...

Strm := TStringStream.Create(APostParams.ToString, TEncoding.UTF8);
try
  FHTTPResponse := HTTPClient.Post(RequestURL, Strm);
finally
  Strm.Free;
end;
Run Code Online (Sandbox Code Playgroud)

在场景 2 中,调用者必须取得所有权并释放返回的TStringList,例如:

var
  instance: ExampleClass;
  list: TStringList;
...
list := instance.AsStringList;
try
  ... 
finally
  list.Free;
end;
Run Code Online (Sandbox Code Playgroud)

如果您不想将 的所有权转移TStringList给调用者,则ExampleClass必须在内部维护它的所有权,例如:

type
  ExampleClass = class
  private
    FName, FValue: string;
    FList: TStringList;
  public
    constructor Create(AName, AValue: string);
    destructor Destroy; override;

    function AsStringList: TStringList;
  end;

implementation

constructor ExampleClass.Create(AName, AValue: string);
begin
  FValue := AValue;
  FName := AName;
  FList := nil;
end;

destructor ExampleClass.Destroy;
begin
  FList.Free;
  inherited Destroy;
end;

function ExampleClass.AsStringList: TStringList;
begin
  if FList = nil then
    FList := TStringList.Create
  else
    FList.Clear;
  FList.Add(FValue);
  FList.Add(FName);
  Result := FList;
end;
Run Code Online (Sandbox Code Playgroud)

否则,调用者需要创建自己的列表并将其传递给类来填充,例如:

type
  ExampleClass = class
  private
    ...
  public
    ...
    procedure AsStringList(AList: TStrings);
  end;

implementation

procedure ExampleClass.AsStringList(AList: TStrings);
begin
  AList.Add(FValue);
  AList.Add(FName);
end;
Run Code Online (Sandbox Code Playgroud)
var
  instance: ExampleClass;
  list: TStringList;
...
list := TStringList.Create;
try
  ...
  instance.AsStringList(list);
  ...
finally
  list.Free;
end;
Run Code Online (Sandbox Code Playgroud)