Reputation: 431
I have read that if a class implements an interface it is now reference counted and should not manage its memory by calling free
.
However, if you make a custom control and have it implement an interface, how do you prevent its owner
from managing its memory? For example when you drop it on a form at design time will the reference counting and owner memory management fight?
Thanks for your time.
Upvotes: 4
Views: 1701
Reputation: 76537
Controls are except from this behavior, because they do not inherit from TInterfacedObject
.
As such they do not do reference counting, their ref count is stuck at -1 by design*).
All controls inherit from TComponent
which looks like this:
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
The reference counting in TComponent looks like this:
function TComponent._AddRef: Integer;
begin
if FVCLComObject = nil then Result := -1
// -1 indicates no reference counting is taking place
else Result := IVCLComObject(FVCLComObject)._AddRef;
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;
*)The caveat being that if VCLComObject is assigned, they follow that Object's reference counting (which is normally not stuck at -1).
VCLComObject
is nil for most components.
It is only used for component wrappers generated by the IDE to wrap COM objects.
See: TComponent.ComObject
So you can add interfaces to your heart's content. It will work with no problems, as long as you remember to free the Component when you're done.
You can test to see if a control does reference count or not by testing:
DoesNotRefCount:= Supports(MyObject, IInterfaceComponentReference)
and (TComponent(MyObject).VCLComObject = nil);
Do not call _AddRef
on your object to test if it returns -1, because that may break objects that do use ref counting.
If your ref counted objects starts at 0 and you do an _AddRef
followed by a _Release
you will destroy the object, even though Delphi was just about to call _AddRef
two instructions further down the line.
If you want to make your own objects that don't do reference counting, it may be a good idea to add a marker interface as well:
INoRefCounting = interface
['{CAD60ADF-C49A-46FB-BB5A-CC54BD22C7EB}']
end;
In newer Delphi's you can descent from TSingletonImplementation
which does the dummy no-ref counting for you.
In older Delphi's descent from TObject (or TWhatever) and implement the no-ref counting like so:
function TMyObject.QueryInterface(const IID: TGUID; out Obj): HResult; {stdcall;}
begin
if GetInterface(IID, Obj) then Result := S_OK
else Result := E_NOINTERFACE;
end;
function TMyObject._AddRef: Integer; {stdcall;}
begin
Result := -1;
end;
function TMyObject._Release: Integer; {stdcall;}
begin
Result := -1;
end;
Final note
If you want to make 100% sure your custom control does not do reference counting, you'll have to override the _AddRef
/_Release
methods to remove the conditional reference counting based on VCLComObject
.
Warning
If the lifetime of your controls is short and you're still holding references to interfaces of those controls for longer then you're going to run into problems.
If you're suffering from this it might be worthwhile to add debugging code to the _AddRef
, _Release
and Destroy
methods where you keep track of reference counts and signal if the refcounts reaches zero to late or too soon.
Upvotes: 6