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:
library MyCars;
uses
CarInterfaces, CarClasses;
function GetMyInterface: IRecyclable; {$IFnDEF WINDOWS} cdecl; {$ELSE} stdcall; {$ENDIF}
begin
Result := TCar.Create as IRecyclable; //"as" optional part just for hint. Recommend this style!
end;
export
GetMyInterface;
end.
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:
unit Loader;
uses
Classes, SysUtils, DynLib, CarInterfaces;
//dont need CarClasses unit, becouse in App not posible use TCar class itself!
//maybe we load library that written on C++. Interfaces cross-language compatabile, objects - not!
type
//defenition same as in library export
TGetMyInterfaceFunc = function(): IRecyclable; {$IFnDEF WINDOWS} cdecl; {$ELSE} stdcall; {$ENDIF}
TLoader = class
private
FLibHandle: TLibHandle;
FGetMyInterfaceFunc: TGetMyInterfaceFunc;
public
constructor Create;
destructor Destroy;
function GetMyInterface: IRecyclable; //can be IInterface. Real type checked in runtime.
end;
constructor TLoader.Create;
var
p: Pointer;
begin
FLibHandle := NilHandle;
FLibHandle := SafeLoadLibrary('libmycars.' + SharedSuffix); //add path if needed
if FLibHandle = NilHandle then raise EException.Create(GetLoadErrorStr);
p := GetProcedureAddress(FLibHandle, 'GetMyInterface');
if p = nil then raise EException.Create('No GetMyInterface function in library!');
FGetMyInterfaceFunc := TGetMyInterfaceFunc(p);
end;
destructor TLoader.Destroy;
begin
if FLibHandle <> NilHandle then UnloadLibrary(FLibHandle);
end;
function TLoader.GetMyInterface: IRecyclable;
begin
Result := GetMyInterfaceFunc(); //at this moment RefCount incremented and object created
end;
And now how to use and control lifecicle of loaded object via interface:
program TestMyCars;
uses
Classes, SysUtils, CarInterfaces, Loader;
//+ Interfaces unit in visual LCL app
procedure PrintRecl(const ALoader: TLoader);
var
ifc: IRecyclable; //object will be created localy and destroyed after this function end.
begin
ifc := ALoader.GetMyInterface; //create object in library and inc RefCount (in local func by 1, but this is not aksioma!)
WriteLn('property isRecyclable = ', ifc.isRecyclable); //inc RefCount for sending as function parameter
//dec RefCount after WriteLn finished
WriteLn('func materialIsRecyclable = ', ifc.materialIsRecyclable()); //inc by 2?
end; //dec RefCount of ifc to zero and Free object automaticly
var
L: TLoader;
begin
L := TLoader.Create;
try
//we can`t load interface here, becouse he will live until the last "end.",
//but library will be unloaded erlier in "finally" block!
PrintRecl(L);
finally
L.Free;
end;
end.
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:
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:
program WrongTestMyCars;
uses
Classes, SysUtils, CarInterfaces, Loader;
var
L: TLoader;
ifc: IRecyclable;
begin
L := TLoader.Create;
try
ifc := L.GetMyInterface; //inc RefCount by 2!!! Becouse "ifc" is global var. Dont do assumptions!
ifc := nil; //dec only by 1!!!
//ifc is still exists, RefCount=1, and object not Free`d! (On fpc 3.2.2)
//This differ from Delphi btw.
finally
L.Free; //at this moment memory of object, that ifc refers wil be freed.
end;
end. //at this moment will be AV Error, becouse fpc decrese ifc RefCount to zero and try call Free method that not exists.
We MUST use
=nil only in class destructor with interface field, like:
type
T = class
private
FIfc: IInterface;
public
destructor Destroy;
end;
destructor T.Destroy;
begin
FIfc := nil; //We MUST do this to trigger Free of object before Destroy finished.
end;
P.S. I have sooo much truble with interaces in past :'( And we need more docs and examles too.