genakust
genakust

Reputation: 89

replace an object in a TObjectList with freeing it (Delphi7)

I have a TObjectList with few elements. I have to replace one of them with one new at the same item (without change a count). I do the following:

procedure TForm1.Button1Click(Sender: TObject);
var
  TObj: TObjectList;
  LPoint: TPPoint;
  i: integer;
begin
  TObj:= TObjectList.Create;
  try

    for i:= 0 to 3 do
    begin
        LPoint:= TPPoint.Create(i, i+ 1);
      TObj.Add(LPoint);
    end;

    LPoint:= TPPoint.Create(21, 22);
    TObj.Items[1]:= nil;
    TObj.Items[1]:= LPoint;

    for i:= 0 to 3 do
    begin
        ShowMessage(IntToStr(TPPoint(TObj.Items[i]).X));
    end;

  finally
    TObj.Free;
  end;
end;

My question is: how can I free the replaced element in the memory? The help says "an Objekt will be freed if his Index will be reassigned". Is the command TObj.Items[1]:= nil; enough?

Thanks in advance for any information.

Upvotes: 2

Views: 977

Answers (2)

Etsitpab Nioliv
Etsitpab Nioliv

Reputation: 424

TObjectList.Create() takes care of its items memory management, when created with OwnsObjects = true (default behavior)

I tested it using Delphi XE7 so I cannot guarantee this is exactly the same behavior in Delphi 7. For example, declaring TObj: TObjectList without a type specified is impossible (won't compile).

I used TLabel instead of TPPoint, and renamed the variables so that it is less confusing. To make sure it is freed, I added the ReportMemoryLeaksOnShutdown := True to my dpr. It's definitely a must have to make sure you don't mess with memory. Sadly it appeared in Delphi 2006, so it's not available in Delphi 7.

This code replaces an item in the list without any memory leaks :

procedure TForm3.Button1Click(Sender: TObject);
var
  list: TObjectList<TLabel>;
  listItem: TLabel;
  i: integer;
begin
  list:= TObjectList<TLabel>.Create();
  try

    for i:= 0 to 3 do
    begin
        listItem:= TLabel.Create(nil);
        listItem.Caption := 'list item ' + IntToStr(I);
        list.Add(listItem);
    end;

    listItem:= TLabel.Create(nil);               
    listItem.Caption := 'list item replaced';
    list.Items[1]:= listItem;

    for i:= 0 to 3 do
    begin
        ShowMessage(list.Items[i].Caption);
    end;
  finally
    list.Free;
  end;
end;

Upvotes: 1

David Heffernan
David Heffernan

Reputation: 612784

TObj.Items[1]:= nil;
TObj.Items[1]:= LPoint;

Here you perform two assignments, so the class attempts to free two items.

TObj.Items[1]:= nil;

At this point the previous item is a non-nil reference added in your earlier loop. That object is thus destroyed.

TObj.Items[1]:= LPoint;

When this line executes, TObj.Items[1] = nil and so the Free method is called on nil. Nothing happens.

The bottom line is that your code is overcomplicated. You can replace

TObj.Items[1]:= nil;
TObj.Items[1]:= LPoint;

with

TObj.Items[1]:= LPoint;

The class will destroy the object currently stored in TObj.Items[1] and then replace it with LPoint. Just as you want.

Upvotes: 3

Related Questions