Jump to content
David Hoyle

IComparer Interface not being released

Recommended Posts

I wonder if anyone can either confirm I'm not going insane and this is a bug I need to report or tell me what I'm doing wrong.

Eurekalog has found a bug in my application and I've managed to decant it down to the following code - it takes a pair of integers and sorts them by the FPosition field (code to get the display order of a VirtualStringTree).

The commented out line in Test() should not be required in my mind as the IComparer interface should be released when it goes out of scope at the end of the Test() function but it doesn't for me (I'm using 10.3.2).

Program ComparerFreeing;

{$APPTYPE CONSOLE}

{$R *.res}


Uses
  System.SysUtils,
  System.Generics.Collections,
  System.Generics.Defaults;

Type
  TXTColumnPosition = Record
    FIndex, FPosition : Integer;
  End;

  TXTColumnPositionComparer = Class(TInterfacedObject, IComparer<TXTColumnPosition>)
  Public
    Constructor Create;
    Destructor Destroy; Override;
    Function Compare(Const Left, Right : TXTColumnPosition) : Integer;
  End;

Function TXTColumnPositionComparer.Compare(Const Left, Right: TXTColumnPosition): Integer;

Begin
  Result := Left.FPosition - Right.FPosition;
End;

Constructor TXTColumnPositionComparer.Create;

Begin
  WriteLn('Create');
  Inherited;
End;

Destructor TXTColumnPositionComparer.Destroy;

Begin
  WriteLn('Destroy');
  Inherited;
End;

Function Test : TArray<TXTColumnPosition>;

Var
  Comparer: TXTColumnPositionComparer;

Begin
  SetLength(Result, 5);             //
  Result[0].FIndex := 0;            //
  Result[0].FPosition := 4;         //
  Result[1].FIndex := 1;            //
  Result[1].FPosition := 3;         //
  Result[2].FIndex := 2;            // Dummy setup for testing
  Result[2].FPosition := 2;         //
  Result[3].FIndex := 3;            //
  Result[3].FPosition := 1;         //
  Result[4].FIndex := 5;            //
  Result[4].FPosition := 0;         //
  Comparer := TXTColumnPositionComparer.Create;
  System.Generics.Collections.TArray.Sort<TXTColumnPosition>(Result, Comparer);
  //: @debug Comparer.DisposeOf; // Required to release the interface 
End;

Var
  aInts : TArray<TXTColumnPosition>;
  i : Integer;

Begin
  Try
    aInts := Test;
    For i := Low(aInts) To High(aInts) Do
      WriteLn(aInts[i].FIndex, ' => ', aInts[i].FPosition);
    ReadLn;
  Except
    On E: Exception Do
      Writeln(E.ClassName, ': ', E.Message);
  End;

End.

I get the following output from the above...

Create
5 => 0
3 => 1
2 => 2
1 => 3
0 => 4

i.e. Destroy is not called.

Share this post


Link to post

Mixing of object and interface references with a const interface param. Solution: declare the Comparer var as IComparer<TXTColumnPosition>

  • Like 1

Share this post


Link to post

Thank's Stefan. Originally I had the following...

  System.Generics.Collections.TArray.Sort<TXTColumnPosition>(Result, TXTColumnPositionComparer.Create);

and that would release. I changed it while trying to find out why. I'll change it as you suggest (I should have noticed that) and see if it works.

Share this post


Link to post

No, that would not release either - thats one of those bugs people keep reporting since ages and Embarcadero refuse to fix - I think @Dalija Prasnikar remembers the according QP entry.

 

FWIW unless the comparer needs its own state you don't even need to create your own class, just pass a function(const left, right: T): Integer to TComparer<T>.Construct

 

fwiw in Spring I added overloads to all methods taking IComparer<T> to also directly accept that - makes using them way more comfortable.

Edited by Stefan Glienke

Share this post


Link to post

The const param reference counting bug that keeps on giving.

 

https://stackoverflow.com/a/31028671/505088

https://stackoverflow.com/a/7640979/505088

https://stackoverflow.com/q/4509015/505088

 

QC report 90482 and presumably many others. Of course, Emba killed QC. Thanks so much for that. Can I be arsed to re-enter all the unsolved reports that were killed when QC was killed? No I cannot.

  • Like 1

Share this post


Link to post

It's beyond me why, if it's never getting fixed, why they can't at least add a compiler warning. We don't even have warnings for the most obvious mistakes like using an uninitialized return value or this. I don't understand why.

Share this post


Link to post
1 hour ago, Der schöne Günther said:

It's beyond me why, if it's never getting fixed, why they can't at least add a compiler warning. We don't even have warnings for the most obvious mistakes like using an uninitialized return value or this. I don't understand why.

If they add warning they could just as well fix it...

 

Seriously, no matter how hard I try, I cannot figure out the real reason why... 

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

×