Reputation: 1241
I know an interfaced object is reference counted, so need not to manually free it. But if it has a TObject inherited member, should I manually free this member in the destructor?
Consider the following code:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Classes;
type
ITestInterface = interface(IInvokable)
['{A7BDD122-7DC6-4F23-93A2-B686571AB2C8}']
procedure TestMethod;
end;
TTestObj = class(TInterfacedObject, ITestInterface)
constructor Create;
destructor Destroy; override;
private
FData: TStrings;
public
procedure TestMethod;
end;
{ TTestObj }
constructor TTestObj.Create;
begin
FData := TStringList.Create;
end;
destructor TTestObj.Destroy;
begin
Writeln('Destroy'); // This line won't apear in the console ouput as the destructor won't be called.
FData.Free; // Who guarantees this member will be freed ?
inherited;
end;
procedure TTestObj.TestMethod;
begin
Writeln('TestMethod');
end;
{ Main }
procedure Main;
var
TestObj: TTestObj;
begin
TestObj := TTestObj.Create;
TestObj.TestMethod;
TestObj := nil; // TestObj should be freed at this moment ?
end;
begin
Writeln('Program start!');
Main;
Writeln('Program end.');
Readln;
end.
The program output:
Program start!
TestMethod
Program end.
It means the constructor was not been called, and the member FData
was not been freed ?
What should I do ? Thanks in advance.
Upvotes: 3
Views: 1379
Reputation: 613342
The code in TTestObj
is fine. You must implement a destructor that destroys FData
just as you have done.
The problem lies elsewhere. The interfaced object is not being destroyed because you never trigger any reference counting. You need to refer to the interfaced object via an interface variable. Replace
TestObj: TTestObj
with
TestObj: ITestInterface
Once you make this change, the interface reference code will add a reference when you first assign to the TestObj
variable.
As an aside, you don't need this line
TestObj := nil
When the TestObj
variable goes out of scope, the reference count will drop to zero and the implementing object will be destroyed.
Upvotes: 8
Reputation: 28826
You will have to free the TStrings
in the destructor (unless you use an ARC compiler), as always.
The parent object (TTestObject
) is freed automatically -- but only if it is used as interface --, but not the objects it references, like FData
.
You are using the object as object, but you must use it as interface to get automatic reference counting:
var
TestObj: ITestInterface;
But it is irrelevant whether TTestObject
implements the interface or not, you must always free any aggregated objects in the destructor, as you do.
Again, the above is true only if you are using a compiler that does not implement ARC for objects (i.e. if you target Win32, Win64, macOS).
Upvotes: 4