Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Delphi manage memory allocated for Objects created as parameters?

The title might seem a little confusing, but the heart of my question is: What happens to the memory allocated in both of the following scenarios?

Scenario 1:

var
 FHTTPResponse: IHTTPResponse;

...

FHTTPResponse := HTTPClient.Post(RequestURL, TStringStream.Create(APostParams.ToString, TEncoding.UTF8));

The TStringStream object is created in the parameters list of a function (in this case, THTTPClient.Post). What happens with the memory allocated for it, as I created it but (afaik) I'm not able to free it.

Scenario 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;

When the user calls ExampleClass.AsStringList, my understanding is that I'm not returning a reference to an object, like I would if I had a field FAsStringList, but the object itself.

In this scenario, I'd be passing the responsibility of freeing the TStringList object (which feels just wrong). Does anything else happen underneath this return? Is there a better approach to return the data as different structures, that doesn't imply giving the user the responsibility of freeing the object without them even knowing they have to do so?

TL;DR Is there a better (or a correct) approach to returning data representation as different object types without transferring memory management responsibilities to the user?

like image 890
Arthur Araujo Avatar asked Mar 17 '26 09:03

Arthur Araujo


1 Answers

Object-type variables/parameters/returns/etc in Delphi are references to objects, not actual objects themselves. Objects reside only in dynamic memory and cannot be passed around by value, only by reference.

Any call to Create() an object type must have a matching call to Free() (or Destroy()) to free the object from memory, otherwise the object is leaked.

So, in Scenario 1, the TStringStream is leaked if HTTPClient.Post() does not take ownership of it and free it. And in Scenario 2, the TStringList is leaked if the caller of AsStringList() does not take ownership of the returned list and free it.

In Scenario 1, let's assume Post() does not take ownership of the stream (seems like a reasonable assumption). The caller should maintain ownership of the stream and free it after Post() returns, eg:

var
  FHTTPResponse: IHTTPResponse;
  Strm: TStringStream;

...

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

In Scenario 2, the caller must take ownership and free the returned TStringList, eg:

var
  instance: ExampleClass;
  list: TStringList;
...
list := instance.AsStringList;
try
  ... 
finally
  list.Free;
end;

If you don't want to transfer ownership of the TStringList to the caller, then ExampleClass must maintain ownership of it internally, eg:

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;

Otherwise, the caller would need to create its own list and pass it to the class to fill in, eg:

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

implementation

procedure ExampleClass.AsStringList(AList: TStrings);
begin
  AList.Add(FValue);
  AList.Add(FName);
end;
var
  instance: ExampleClass;
  list: TStringList;
...
list := TStringList.Create;
try
  ...
  instance.AsStringList(list);
  ...
finally
  list.Free;
end;
like image 123
Remy Lebeau Avatar answered Mar 20 '26 09:03

Remy Lebeau



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!