1

Say my form is delared as TFormOther = class(TForm, IMyInterface) where

type
  IMyInterface = interface
    ['{50E7E215-A8EA-4A1C-9F1E-018E4A76DCBD}']
    procedure DoSomething;
  end;

and

TFactory = class(TInterfacedObject)
public
  procedure MakeIt;
end;

procedure TFactory.MakeIt;
var
  LMyIntf: IMyInterface;
begin
  LMyIntf :=  TFormOther.Create(nil);

  LMyIntf.DoSomething;

  // (LMyIntf as TFormOther).Free; This is wrong and gives the classic: 
  // FastMM has detected an attemp to use an interface of a freed object.
end;

If I don't free the TFormOther instance I leak memory.

I know I can do Action := TCloseAction.caFree in TFormOther.FormClose but is that the only and best way?

This Will an interface-implementing form free itself when there are no more references to it? helped a lot but did not say how one should free the form.

Jaco Crafford
  • 13
  • 1
  • 3
  • Where does the form implement rfernece counted lifetime management? – David Heffernan Oct 17 '22 at 18:24
  • @David I think I talked rubbish as @Remy mentioned below: `TComponent` which TForm derives from, disables reference counting. – Jaco Crafford Oct 18 '22 at 07:59
  • So you could just enable reference counting. Or you hold the reference in a TFormOther variable and then call `Free` on it. Which I think is what I would do, if that fits with the rest of your code. If it doesn't then I'd add reference counting to the class. – David Heffernan Oct 18 '22 at 12:16

1 Answers1

0

Problem with directly freeing form through its interface reference with (LMyIntf as TFormOther).Free; is that interface reference will outlive form object instance.

When that interface goes out of scope, in the procedure epilogue, compiler inserted call to _IntfClear to finalize LMyIntf reference will eventually end up calling _Release method on already destroyed form instance.

To avoid such scenario, you must explicitly clear the interface before you can free form. This requires additional object variable through which you can call Free to free the form.

procedure TFactory.MakeIt;
var
  LMyIntf: IMyInterface;
  LObj: TObject; 
begin
  LMyIntf :=  TFormOther.Create(nil);

  LMyIntf.DoSomething;

  LObj := TObject(LMyIntf);
  LMyIntf := nil;
  LObj.Free;
end; 

When it comes to releasing the form through FromClose event handler, such release will work flawlessly only if there are no active interface references to the form at the time of the release. It is hard to say which approach is better when discussing general code, but when using FormClose event it might be harder to ensure that you don't have active interface at that moment and such code may be harder to follow.

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • Be aware of the fact that `TComponent` (which `TForm` derives from) disables interface reference counting (ie, `_AddRef()` and `_Release()` are implemented as no-ops). That is why freeing an interfaced Form object is trickier than freeing non-component interfaced objects, so you have to take more care to do so manually and safely. – Remy Lebeau Oct 17 '22 at 18:23
  • @Dalija What will be the difference if I give 'IMyInterface' a '[weak]' attribute and do a `(LMyIntf as TFormOther).Free;` at the botton. – Jaco Crafford Oct 18 '22 at 07:56
  • If you mark interface reference as `[weak]` then this interface reference will be cleared during the form destruction process and it will not cause any problems. If you want to use this approach marking interface reference as [unsafe] is even better as such interface reference will be excluded from reference counting and there will not be any `_IntfClear` calls in procedure epilogue. However, using `[weak]` and `[unsafe]` attributes breaks code completion and some other IDE related functionality. – Dalija Prasnikar Oct 18 '22 at 09:08
  • Personally breaking the IDE is the reason I am avoiding attributes in methods as much as possible if I can use some workaround even if it requires a bit more code. I avoid it so much, that I even keep forgetting that `[weak] and `[unsafe]` can be used as a solution for some problems ;) – Dalija Prasnikar Oct 18 '22 at 09:12