Jump to content
Trevor S

Destroying TList with Managed Types

Recommended Posts

Can anyone shed light on why the a TList<> (Generics) containing Interfaced or other  managed type does not de-reference the items when the list is destroyed.  You must explicitly call the Clear method on list before freeing in order to prevent memory leaks.

 

I would expect that simply calling Free on the list should de-reference the items before destroying the list.

 

Any ideas as to whether or not this behavior is intentional?  

 

program Test.TList.Disposal;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  Generics.Collections;

type
  IItem = Interface
    ['{9BA33670-15FF-4169-9CBD-626857A3E47F}']
  End;

  TItem = Class( TInterfacedObject, IItem)
  public
    constructor Create;
    destructor Destroy; override;
  End;

  TItemList = Class( TList<IItem>);

constructor TItem.Create;
begin
  inherited Create;
  Writeln( 'Item Created');
end;

destructor TItem.Destroy;
begin
  Writeln( 'Item Destroyed');
  inherited;
end;

procedure TestDestroyOnly;
var
  List: TItemList;
begin
  Writeln( 'Start Test - Destroy Only');
  List := TItemList.Create;
  try
    List.Add( TItem.Create as IItem);

  finally
    List.Free;
  end;
  Writeln( 'End Test - Destroy Only');
end;

procedure TestWithExplicitClear;
var
  List: TItemList;
begin
  Writeln( 'Start Test - Explicit Clear');
  List := TItemList.Create;
  try
    List.Add( TItem.Create as IItem);

    List.Clear;
  finally
    List.Free;
  end;
  Writeln( 'End Test - Explicit Clear');

end;

begin
  try
    TestDestroyOnly;
    TestWithExplicitClear;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Edited by Trevor S

Share this post


Link to post

Forget it, I just realised that it does finally release the Items at the end of the method rather that in the list destruction, where as List.Clear releases the items immediately in the Clear method :classic_blush:

 

 

Edited by Trevor S

Share this post


Link to post

That is not the reason.  Your test functions may simply be holding a hidden local reference to the IItem that you create and add to the list, so the item is not fully released until after the list has been freed first.  Try this instead to isolate that reference so it gets released sooner:

procedure TestDestroyOnly;
var
  List: TItemList;
  procedure AddItem;
  begin
    List.Add(TItem.Create as IItem);
  end;
begin
  Writeln( 'Start Test - Destroy Only');
  List := TItemList.Create;
  try
    AddItem;
  finally
    List.Free;
  end;
  Writeln( 'End Test - Destroy Only');
end;

procedure TestWithExplicitClear;
var
  List: TItemList;
  procedure AddItem;
  begin
    List.Add(TItem.Create as IItem);
  end;
begin
  Writeln( 'Start Test - Explicit Clear');
  List := TItemList.Create;
  try
    AddItem;
    List.Clear;
  finally
    List.Free;
  end;
  Writeln( 'End Test - Explicit Clear');
end;

 

Edited by Remy Lebeau
  • Thanks 1

Share this post


Link to post
1 hour ago, Remy Lebeau said:

List.Add(TItem.Create as IItem);

Isn't that supposed to be a no-no.

I seem to recall there's a problem with premature release when creating an instance as a parameter if the parameter is declared const.

 

Edit: Never mind. The as takes care of that problem.

Edited by Anders Melander
never mind

Share this post


Link to post
1 hour ago, Anders Melander said:

Isn't that supposed to be a no-no.

No, because the as operator forces the compiler to create a hidden variable for the interface, before it is then passed to the const parameter.  Had the code been written like this instead, THEN there would have been a problem:

List.Add(TItem.Create);
1 hour ago, Anders Melander said:

Edit: Never mind. The as takes care of that problem.

Correct.

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×