Why TForm's _release method does not call destructor?
var
F, U : IUnknown;
procedure TForm1.btn1Click(Sender: TObject);
begin
U := IUnknown(TMyObject.Create); // MyIterfacedObject (inherits TInterfacedObject)
F := IUnknown(TMyForm.Create(nil));
end;
procedure TForm1.btn2Click(Sender: TObject);
begin
U := nil; // Calls destructor
F._Release; // Does not call destructor
F := nil; // Does not call destructor
end;
I took a look at _release methods of TInterfaceObject and TComponent classes:
function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
function TComponent._Release: Integer;
begin
if FVCLComObject = nil then
Result := -1 // -1 indicates no reference counting is taking place
else
Result := IVCLComObject(FVCLComObject)._Release;
end;
TInterfacedObject's _release seems quite intelligible, but what TComponent's _release do? Seems weird to me...
The reason is that
TComponent
adopts the policy that lifetime management is the responsibility of the user of the class, and is not to be managed automatically by any interface references taken. That policy is expressed clearly inTComponent._Release
.The common scenario, the one which you describe, has
FVCLComObject
equal tonil
. And so the code explicitly says that there is no reference counting by returning-1
. It's even commented that way.Lifetime management needs to be done one way or another. The two commonly used patterns with Delphi code are:
Lifetime managed by caller
Although,
TComponent
is often used a little differently from this in that its constructor is passed an owner component. And this owner is then charged with the responsibility of destroying the owned component. So that pattern looks like this:Lifetime managed by interface references
You cannot mix the two patterns. The design choice was made that
TComponent
would follow the first pattern. And so the interface reference counting must be disabled. By way of contrast,TInterfacedObject
adopts the other policy.