I want to build a class TParent
containing several child objects by using aggregation. Some of the objects are independant, while some can be also dependant on the other children. All the children objects have to have a reference to the parent. I also want to use the interfaces where possible.
For this purpose I am using TInterfacedObject
for the TParent
and TAggregatedObject
for the children. As both the child and the parent know about each other, I am using weak references in order to avoid the circuar dependency. In fact this behaviour is already defined in TAggregatedObject
. Everything works fine when I am using only the independant child objects (TIndependantChild
).
The problem arises when the child object depends on the other children too, see the constructor of TDependantChild
. I store the reference to the another child object in fChild variable, which is marked with the [weak]
attibute, introduced in Delphi 10 Berlin. FastMM4 reports memory leaks on shutdown:
Also access violation leading to System.TMonitor.Destroy
raises, but this happens only when FastMM4 is in the uses and ReportMemoryLeaksOnShutDown is True.
program Project1;
{$APPTYPE CONSOLE}
uses
FastMM4,
System.SysUtils;
type
IParent = interface
['{B11AF925-C62A-4998-855B-268937EF30FB}']
end;
IChild = interface
['{15C19A4E-3FF2-4639-8957-F28F0F44F8B4}']
end;
TIndependantChild = class(TAggregatedObject, IChild)
end;
TDependantChild = class(TAggregatedObject, IChild)
private
[weak] fChild: IChild;
public
constructor Create(const Controller: IInterface; const AChild: IChild); reintroduce;
end;
TParent = class(TInterfacedObject, IParent)
private
fIndependantChild: TIndependantChild;
fDependantChild: TDependantChild;
public
constructor Create;
destructor Destroy; override;
end;
{ TParent }
constructor TParent.Create;
begin
fIndependantChild := TIndependantChild.Create(Self);
fDependantChild := TDependantChild.Create(Self, fIndependantChild);
end;
destructor TParent.Destroy;
begin
fDependantChild.Free;
fIndependantChild.Free;
inherited;
end;
{ TDependantChild }
constructor TDependantChild.Create(const Controller: IInterface; const AChild: IChild);
begin
inherited Create(Controller);
fChild := AChild;
end;
var
Owner: IParent;
begin
ReportMemoryLeaksOnShutDown := True;
Owner := TParent.Create;
Owner := nil;
end.
I found, that using [unsafe] instead of [weak] solves the problem, but according to the delphi help
It ([unsafe]) should be only used outside the System unit in very rare situations.
Therefore, I am not convinced, that I should use [unsafe]
here, especially when I don't understand what happens.
So, what are the reassons of the memory leaks in this situation and how to overcome them?
Issue with the leak and the crash when you use external FastMM4 memory manager is related to following issue concerning finalization of internal HashMap used for tracking weak references.
[REGRESSION XE2/10.1 Berlin] Unable to use 3rd party memory managers
Due to that issue it is impossible to use 3rd party memory managers for leak detection in Delphi 10.1 and newer versions, and including external FastMM4.
That is the reason why you have issues with [weak]
attribute and you don't have issues with [unsafe]
.
As far as your code is concerned, you can safely use [unsafe]
in above scenario. While there is warning in documentation about using [unsafe]
attribute, that warning actually does not explain why [unsafe]
should not be used.
To make long story short, you can use [unsafe]
attribute when lifetime of object instance referenced by [unsafe]
reference is longer than the lifetime of the reference itself.
In other words, you must ensure that you don't access [unsafe]
reference after the object instance it points to has been released and that is all.
[unsafe]
references are not zeroed out when the object they point to is destroyed, and using such reference after the object is gone will result in access violation exceptions.
Replacing the [weak]
attribute with the [unsafe]
is all you have to do in order to have properly functional code as you presented it.
TDependantChild = class(TAggregatedObject, IChild)
private
[unsafe] fChild: IChild;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With