1

I run in this situation where Destroy() is never called.

unit Unit2;

interface

type

// Interface
ITest = Interface(IInterface)
 function IsTrue() : Boolean;
end;

TmyClass = class(TInterfacedObject, ITest)
  public
    // Interface implementation
    function IsTrue() : Boolean;

    constructor Create();
    destructor Destroy(); override;
end;

implementation

constructor TmyClass.Create();
begin
  inherited Create();
end;

destructor TmyClass.Destroy();
begin
  inherited Destroy();
end;

published
  // Property
  property IsItTrue: Boolean read IsTrue;
end.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, 
  Vcl.Dialogs, Vcl.StdCtrls, unit2;

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure FormCreate(Sender: TObject);
  procedure Button1Click(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  Form1: TForm1;
  fMyClass: TmyClass;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  fMyClass.Free;  // if refcount = 0 this works, if refcount <> 0 pointer error.
  //or
  fMyClass := Nil; // no error but Destroy wil not be executed
  Close();
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fMyClass := TMyClass.Create();
end;
end.

Reading this article, there is only a constructor but no destructor implemented.

Is there any particular reason for this?

And should I release (if needed) all other objects that will be defined in myClass by implementing a finalization section?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
ToKa
  • 80
  • 7
  • The class in the article doesn't have any resources that should be cleaned up and so doesn't need to override the destructor. You should find out why your destructor is not called. – Sertac Akyuz Dec 02 '19 at 14:23
  • Your question as-is is unanswerable. There is no [mcve] so we cannot tell why your destructor is not called. Whether you need to override default destructor is another question and it depends on what kind of fields you have declared in your class and how you are using them. – Dalija Prasnikar Dec 02 '19 at 14:32
  • There seem to be two separate questions here: 1) Does the `TInterfacedObject` need its destructor overridden, and 2) Why your destructor is never called. But the rule of thumb is yes, it will essentially work like other objects for the most part, so if you create / initialize anything in the constructor, or need any cleanup in general, then you need to destroy it in the destructor. Why the destructor is never called, hard to say without seeing enough code. – Jerry Dodge Dec 02 '19 at 14:35
  • `TInterfacedObject` is reference counted. The declaration of your class is fine (provided you only ever deal with `IInterface` variables), but if you don't ever assign object instances of your class to interface variables then the reference count won't be managed correctly so the destructor won't be called. Please show an example of how you are actually using the class, them someone can explain why it is not working properly. – Remy Lebeau Dec 02 '19 at 15:14
  • @Remy The original class and api has to many code to publish here. So, I thought to simplify the issue. Although I must say that the Interface is really an IMFSourceReaderCallback. But implementation of this interface is the same way as in my simple sample. I don't get rid of the referencecount initialized on Create() it stays 2. – ToKa Dec 02 '19 at 21:46
  • @ToKa see the answer I just posted. – Remy Lebeau Dec 02 '19 at 22:09

2 Answers2

6

The most likely reason for the destructor not being called would be because you don't assign your object to an interface variable.

procedure Test1;
var
  vMyObj : TObject;
begin
  vMyObj := myclass.Create;
end; <-Destructor NOT called here

procedure Test2;
var
  vMyIntf : IInterface;
begin
  vMyIntf := myclass.Create;
end; <-Destructor IS called here.

If that's the case, I invite you to read this answer for more information.

Ken Bourassa
  • 6,363
  • 1
  • 19
  • 28
  • If this guess is correct, then the question is dupe – David Heffernan Dec 02 '19 at 16:01
  • The declaration is 'var fMyClass: myClass;' and it is created as follows: 'fmyClass := MyClass.Create();' Embarcadero says: "use TInterfacedObject as a base class because it implements the methods of IInterface." So, that is what I did though? – ToKa Dec 02 '19 at 16:35
  • 1
    @ToKa Interface variables and object variables are 2 different things. Whether the variable is declared as TObject or as myclass doesn't change the fact that it is still an object variable and not an interface one. If you change your declaration to `var fMyClass : IInterface`, you will see your destructor is properly called when the variable is set to nil (either implicitly, or explicitly) – Ken Bourassa Dec 02 '19 at 16:47
  • @Ken I think we have a misunderstanding here. For instance: If I would add a field in the public field of the class, like 'sMyFileName: string;', and in the form a call like 'fMyClass.sMyFileName := 'True.txt'', the compiler reports: "fMyClass.sMyFileName does not contain a member etc..". The issue with the original source code, which is to big to drop here, keeps the reference count on 2. The reference count is initialized directly when Create() is initialized. All other objects initialized are counting up but can be freed before destruction. >> – ToKa Dec 02 '19 at 21:41
  • >> However.. the initial reference count stays 2 and that will generate a pointer error on destruction. I have no clue why. – ToKa Dec 02 '19 at 21:41
  • @ToKa I believe you don't quite understand what interface are, how they work and how they should be used, which is quite beyond the scope of your question. From your edit, you seem to expect `fMyClass := Nil;` to call the destructor, something that definitely should NOT happen. And you say you "initialize" the reference count to 2? After `fMyClass := TMyClass.Create();`, the reference count of your object should normally be 0. (That is, unless you have some weird reference to `self` within your object...) – Ken Bourassa Dec 02 '19 at 22:09
  • In `fMyClass := TMyClass.Create();` the initial reference count should be 0 if `fMyClass` is an object variable, and 1 if `fMyClass` is an interface variable. Either way, the reference count should definitely not be 2. That implies there is another reference somewhere that is not demonstrated in this code. – Remy Lebeau Dec 02 '19 at 22:13
  • At this point, I'm not excluding the possibility he manually sets the reference count to 2 in the constructor. – Ken Bourassa Dec 02 '19 at 22:43
  • @Ken Sorry, I know a lot about interfaces, but a little less how Delphi is dealing with them. But that shouldn't be hard to learn after C++, I hope. – ToKa Dec 03 '19 at 00:37
  • @Remy, I explained above the original implementation is the IMFSourceReadeCallback interface, from the Media Foundation API. I had to make things a bit simple here, because -I thought- it would be more readable, instead of publishing the whole API, form and class units. – ToKa Dec 03 '19 at 00:37
  • But all thanks for helping me with struggling through Delphi issues :-) – ToKa Dec 03 '19 at 00:39
1

Your fMyClass variable is an object reference, not an interface, so it does not participate in TInterfaceObject's reference counting.

You need to change this:

fMyClass: TmyClass;

to this:

fMyClass: ITest;

And then you can get rid of fMyClass.Free; altogether:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, 
  Vcl.Dialogs, Vcl.StdCtrls, unit2;

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure FormCreate(Sender: TObject);
  procedure Button1Click(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  Form1: TForm1;
  fMyClass: ITest;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  fMyClass := nil; // no error and Destroy will be executed
  Close();
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fMyClass := TMyClass.Create();
end;

end.

fMyClass := nil; will invoke reference counting only if fMyClass is an interface variable, not an object reference, and you can't call Free() on an interface variable.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770