Recent

Author Topic: Correct Way of Freeing an interface  (Read 810 times)

Amir61

  • New Member
  • *
  • Posts: 49
    • http://Amir.Aavani.net
Correct Way of Freeing an interface
« on: March 07, 2025, 01:42:40 pm »
Hi,

I have an interface and a few classes implementing that interface.

I want to have a function that takes an object of type my interface, do some processing and at the end free the object.

  Is the following code safe/correct?

Code: Pascal  [Select][+][-]
  1. function F(i: IMyInterface): Boolean;
  2. begin
  3.    ...
  4.     TObject(i).Free;
  5. end;
  6.  

cdbc

  • Hero Member
  • *****
  • Posts: 2862
    • http://www.cdbc.dk
Re: Correct Way of Freeing an interface
« Reply #1 on: March 07, 2025, 02:13:11 pm »
Hi
Hell No!
If said interface is a COM interface:
Code: Pascal  [Select][+][-]
  1. function F(i: IMyInterface): Boolean;
  2. begin
  3.    ...
  4.     TObject(i).Free;
  5. end;
This will screw up your refcounting and produce AV when the compiler tries to destroy the underlying object.
If said interface is a COM interface, then the correct way is to forget about it, because it will only live as long as the scope of the function/procedure.
The /Sender/ -- caller object will still be alive after the function call, at which point you can set it to nil:
Code: Pascal  [Select][+][-]
  1. MyInterface:= nil;
which tells the compiler "I'm done with it and if no one else is using the interface then free it"
The mantra is: If you use COM interfaces, then don't intermix them with classes, stay with interfaces!!! They are automagically memory-managed by the compiler!
Please tell us, what you want to do, so we can help steer you in the right direction...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Amir61

  • New Member
  • *
  • Posts: 49
    • http://Amir.Aavani.net
Re: Correct Way of Freeing an interface
« Reply #2 on: March 07, 2025, 05:44:28 pm »
The classes implementing the interface are inheriting from TInterfacedObject and IMyInterface (similar to https://wiki.lazarus.freepascal.org/Understanding_Interfaces).

Assume my function gets an instance of
Code: Pascal  [Select][+][-]
  1.  IRecyclable
and wants to free the object.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1598
    • Lebeau Software
Re: Correct Way of Freeing an interface
« Reply #3 on: March 07, 2025, 06:01:37 pm »
Assume my function gets an instance of
Code: Pascal  [Select][+][-]
  1.  IRecyclable
and wants to free the object.

Sounds like you want something similar to .NET's IDisposable, where you can tell an object to dispose its internal resources without freeing the object itself, eg:

Code: Pascal  [Select][+][-]
  1. function F(i: IMyInterface): Boolean;
  2. var
  3.   rIntf: IRecyclable;
  4. begin
  5.    ...
  6.   if Supports(i, IRecyclable, rIntf) then
  7.     rIntf.Recycle;
  8. end;

That said, interfaces are normally reference-counted (handled by TInterfacedObject), so just let the reference counter do its job.  If your function really wants to free such an object, it needs to release any pre-existing interface references to the object.  In your example, that means it should accept the caller's variable by var reference so it can clear that reference, eg:

Code: Pascal  [Select][+][-]
  1. function F(var i: IMyInterface): Boolean;
  2. begin
  3.    ...
  4.     i := nil;
  5. end;
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Raskaton

  • New Member
  • *
  • Posts: 27
Re: Correct Way of Freeing an interface
« Reply #4 on: March 08, 2025, 12:55:32 am »
Localize all work with interface in one function. Then object will be Freed automaticly after func end.

For example, intarfaces mainly used to export Objects/classes from library. Lets assume, that we have unit CarClasses with class TCar and unit CarInterfaces with defenition IRecyclable from: https://wiki.lazarus.freepascal.org/Understanding_Interfaces.

Library code:
Code: Pascal  [Select][+][-]
  1. library MyCars;
  2. uses
  3.   CarInterfaces, CarClasses;
  4.  
  5. function GetMyInterface: IRecyclable; {$IFnDEF WINDOWS} cdecl; {$ELSE} stdcall; {$ENDIF}
  6. begin
  7.   Result := TCar.Create as IRecyclable; //"as" optional part just for hint. Recommend this style!
  8. end;
  9.  
  10. export
  11.   GetMyInterface;
  12. end.
  13.  
We create object and send all control on him to auto refcount mechanism via IInterface.
At the end of export function RefCount > 0 (not always exactly 1, may be 2 and more).
Because TCar childe of TInterfacedObject, it will be destroyed when RefCount=0.

In application we need load dyn library and expose GetMyInterface function:
Code: Pascal  [Select][+][-]
  1. unit Loader;
  2. uses
  3.   Classes, SysUtils, DynLib, CarInterfaces;
  4.   //dont need CarClasses unit, becouse in App not posible use TCar class itself!
  5.   //maybe we load library that written on C++. Interfaces cross-language compatabile, objects - not!
  6.  
  7. type
  8.   //defenition same as in library export
  9.   TGetMyInterfaceFunc = function(): IRecyclable; {$IFnDEF WINDOWS} cdecl; {$ELSE} stdcall; {$ENDIF}
  10.  
  11.   TLoader = class
  12.   private
  13.     FLibHandle: TLibHandle;
  14.     FGetMyInterfaceFunc: TGetMyInterfaceFunc;
  15.   public
  16.     constructor Create;
  17.     destructor Destroy;
  18.     function GetMyInterface: IRecyclable; //can be IInterface. Real type checked in runtime.
  19.   end;
  20.  
  21. constructor TLoader.Create;
  22. var
  23.   p: Pointer;
  24. begin
  25.   FLibHandle := NilHandle;
  26.   FLibHandle := SafeLoadLibrary('libmycars.' + SharedSuffix); //add path if needed
  27.   if FLibHandle = NilHandle then raise EException.Create(GetLoadErrorStr);
  28.  
  29.   p := GetProcedureAddress(FLibHandle, 'GetMyInterface');
  30.   if p = nil then raise EException.Create('No GetMyInterface function in library!');
  31.   FGetMyInterfaceFunc := TGetMyInterfaceFunc(p);
  32. end;
  33.  
  34. destructor TLoader.Destroy;
  35. begin
  36.   if FLibHandle <> NilHandle then UnloadLibrary(FLibHandle);
  37. end;
  38.  
  39. function TLoader.GetMyInterface: IRecyclable;
  40. begin
  41.   Result := GetMyInterfaceFunc(); //at this moment RefCount incremented and object created
  42. end;
  43.  

And now how to use and control lifecicle of loaded object via interface:
Code: Pascal  [Select][+][-]
  1. program TestMyCars;
  2. uses
  3.   Classes, SysUtils, CarInterfaces, Loader;
  4.   //+ Interfaces unit in visual LCL app
  5.  
  6. procedure PrintRecl(const ALoader: TLoader);
  7. var
  8.   ifc: IRecyclable; //object will be created localy and destroyed after this function end.
  9. begin
  10.   ifc := ALoader.GetMyInterface; //create object in library and inc RefCount (in local func by 1, but this is not aksioma!)
  11.   WriteLn('property isRecyclable = ', ifc.isRecyclable); //inc RefCount for sending as function parameter
  12.   //dec RefCount after WriteLn finished
  13.   WriteLn('func materialIsRecyclable = ', ifc.materialIsRecyclable()); //inc by 2?
  14. end; //dec RefCount of ifc to zero and Free object automaticly
  15.  
  16. var
  17.   L: TLoader;
  18. begin
  19.   L := TLoader.Create;
  20.   try
  21.     //we can`t load interface here, becouse he will live until the last "end.",
  22.     //but library will be unloaded erlier in "finally" block!
  23.     PrintRecl(L);
  24.   finally
  25.     L.Free;
  26.   end;
  27. end.
  28.  

This is complex example, but even if you dont use library, main line is same: Create separate function in with load&use interface.

Also don`t use trick with:
Code: Pascal  [Select][+][-]
  1. ifc := nil;
  2.  
it is not always do as you think. In some cases RefCount will be incrased by 2, and doing =nil decrese it only by 1!
Last decrase will be after "end".

Lets modife example:
Code: Pascal  [Select][+][-]
  1. program WrongTestMyCars;
  2. uses
  3.   Classes, SysUtils, CarInterfaces, Loader;
  4.  
  5. var
  6.   L: TLoader;
  7.   ifc: IRecyclable;
  8. begin
  9.   L := TLoader.Create;
  10.   try
  11.     ifc := L.GetMyInterface; //inc RefCount by 2!!! Becouse "ifc" is global var. Dont do assumptions!
  12.     ifc := nil; //dec only by 1!!!
  13.     //ifc is still exists, RefCount=1, and object not Free`d! (On fpc 3.2.2)
  14.     //This differ from Delphi btw.
  15.   finally
  16.     L.Free; //at this moment memory of object, that ifc refers wil be freed.
  17.   end;
  18. end. //at this moment will be AV Error, becouse fpc decrese ifc RefCount to zero and try call Free method that not exists.
  19.  

We MUST use =nil only in class destructor with interface field, like:
Code: Pascal  [Select][+][-]
  1. type
  2.   T = class
  3.   private
  4.     FIfc: IInterface;
  5.   public
  6.     destructor Destroy;
  7.   end;
  8.  
  9. destructor T.Destroy;
  10. begin
  11.   FIfc := nil; //We MUST do this to trigger Free of object before Destroy finished.
  12. end;
  13.  

P.S. I have sooo much truble with interaces in past :'( And we need more docs and examles too.

cdbc

  • Hero Member
  • *****
  • Posts: 2862
    • http://www.cdbc.dk
Re: Correct Way of Freeing an interface
« Reply #5 on: March 08, 2025, 07:39:20 am »
Hi
@Raskaton: Damn nice examples and good description too, Me Likey  8-)
Interfaces and Libraries are a match made in heaven  8)
Good on you mate.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

 

TinyPortal © 2005-2018