Я хочу создать класс TParent
, содержащий несколько дочерних объектов, с помощью агрегирования. Некоторые объекты независимы, а некоторые могут зависеть и от других дочерних элементов. Все дочерние объекты должны иметь ссылку на родительский. Я также хочу использовать интерфейсы, где это возможно.
Для этой цели я использую TInterfacedObject
для TParent
и TAggregatedObject
для детей. Поскольку и ребенок, и родитель знают друг о друге, я использую слабые ссылки, чтобы избежать цепной зависимости. Фактически это поведение уже определено в TAggregatedObject
. Все работает нормально, когда я использую только независимые дочерние объекты (TIndependantChild
).
Проблема возникает, когда дочерний объект зависит также от других дочерних объектов, см. Конструктор TDependantChild
. Я сохраняю ссылку на другой дочерний объект в переменной fChild, которая помечена атрибутом [weak]
, введенным в Delphi 10 Berlin. FastMM4 сообщает об утечках памяти при выключении:
Также возникает нарушение прав доступа, приводящее к System.TMonitor.Destroy
, но это происходит только тогда, когда FastMM4 используется и ReportMemoryLeaksOnShutDown имеет значение 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.
Я обнаружил, что использование [unsafe] вместо [weak] решает проблему, но согласно delphi справка
Его ([небезопасно]) следует использовать вне системного блока только в очень редких случаях.
Поэтому я не уверен, что мне следует использовать здесь [unsafe]
, особенно когда я не понимаю, что происходит.
Итак, каковы причины утечек памяти в этой ситуации и как их преодолеть?
[weak]
был добавлен для интерфейсов в 10.1 Berlin, но[weak]
существует в более ранних версиях. Я могу скомпилировать ваш код как есть в XE2, но[weak]
не действует.Owner
имеетRefCount=1
, потому чтоTDependantChild
имеет неслабую ссылку наTParent
(из-за агрегации) при назначенииfChild
, несмотря на то, что он[weak]
. КогдаOwner
уничтожается,TInterfacedObject.BeforeDestruction()
вызывает ошибку, когдаRefCount<>0
, вызывая утечкуOwner
и его дочерних элементов. ИзменениеfChild
наPointer
исправляет это. Подозреваю нечто подобное в вашем случае при использовании[unsafe]
. Подтвердить с помощью отладчика - person Remy Lebeau   schedule 03.11.2017procedure TInterfacedObject.BeforeDestruction
. Без[weak]
RefCount
действительно равно 1, а с[weak]
это 0. Таким образом, похоже, что это утечка где-то еще. - person VyPu   schedule 03.11.2017