Trevor S 2 Posted May 17, 2020 (edited) 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 May 17, 2020 by Trevor S Share this post Link to post
Trevor S 2 Posted May 17, 2020 (edited) 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 Edited May 17, 2020 by Trevor S Share this post Link to post
Remy Lebeau 1393 Posted May 18, 2020 (edited) 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 May 18, 2020 by Remy Lebeau 1 1 Share this post Link to post
Anders Melander 1782 Posted May 18, 2020 (edited) 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 May 18, 2020 by Anders Melander never mind Share this post Link to post
Remy Lebeau 1393 Posted May 18, 2020 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. 2 Share this post Link to post