Delphi: агрегация объектов и утечки памяти с использованием атрибута [слабый]
Я хочу построить класс TParent
содержащий несколько дочерних объектов с помощью агрегации. Некоторые из объектов независимы, в то время как некоторые могут также зависеть от других детей. Все дочерние объекты должны иметь ссылку на родителя. Я также хочу использовать интерфейсы, где это возможно.
Для этого я использую TInterfacedObject
для TParent
а также TAggregatedObject
для детей. Поскольку и ребенок, и родитель знают друг о друге, я использую слабые ссылки, чтобы избежать зависимости от циркуляра. На самом деле это поведение уже определено в TAggregatedObject
, Все отлично работает, когда я использую только независимые дочерние объекты (TIndependantChild
).
Проблема возникает, когда дочерний объект зависит также от других дочерних элементов, см. Конструктор TDependantChild
, Я храню ссылку на другой дочерний объект в переменной fChild, которая помечена [weak]
attibute, представленный в 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.
Я обнаружил, что использование [небезопасных] вместо [слабых] решает проблему, но согласно справке delphi
Его ([небезопасно]) следует использовать только вне системного блока в очень редких ситуациях.
Поэтому я не убежден, что мне следует использовать [unsafe]
здесь, особенно когда я не понимаю, что происходит.
Итак, каковы причины утечек памяти в этой ситуации и как их преодолеть?
1 ответ
Проблема утечки и сбоя при использовании внешнего диспетчера памяти FastMM4 связана с следующей проблемой, касающейся завершения внутреннего HashMap, используемого для отслеживания слабых ссылок.
[REGRESSION XE2 / 10.1 Berlin] Невозможно использовать сторонние менеджеры памяти
Из-за этой проблемы невозможно использовать сторонние менеджеры памяти для обнаружения утечек в Delphi 10.1 и более новых версиях, включая внешний FastMM4.
Вот почему у вас есть проблемы с [weak]
атрибут, и у вас нет проблем с [unsafe]
,
Что касается вашего кода, вы можете безопасно использовать [unsafe]
в приведенном выше сценарии. Пока в документации есть предупреждение об использовании [unsafe]
атрибут, это предупреждение на самом деле не объясняет, почему [unsafe]
не должен использоваться.
Короче говоря, вы можете использовать [unsafe]
атрибут при жизни экземпляра объекта, на который ссылается [unsafe]
ссылка длиннее, чем время жизни самой ссылки.
Другими словами, вы должны убедиться, что у вас нет доступа [unsafe]
ссылка после того, как экземпляр объекта, на который он указывает, был освобожден, и это все.
[unsafe]
ссылки не обнуляются, когда объект, на который они указывают, уничтожен, и использование такой ссылки после того, как объект исчезнет, приведет к исключениям нарушения доступа.
Замена [weak]
атрибут с [unsafe]
это все, что вам нужно сделать, чтобы иметь правильно работающий код, как вы его представили.
TDependantChild = class(TAggregatedObject, IChild)
private
[unsafe] fChild: IChild;