Вопрос, поднятый в контексте, касается особенностей работы с интерфейсами и подсчетом ссылок в Delphi, а именно поведения агрегирующего объекта, который реализует интерфейс. Пользователь ожидает, что подсчет ссылок должен работать на внешнем агрегирующем объекте, реализующем интерфейс, но сталкивается с проблемой, когда destructor объекта не вызывается.
Пример, демонстрирующий проблему:
program SO16210993;
{$APPTYPE CONSOLE}
type
IFoo = interface
procedure Foo;
end;
TFooImpl = class(TInterfacedObject, IFoo)
procedure Foo;
end;
TContainer = class(TInterfacedObject, IFoo)
private
FFoo: IFoo;
public
constructor Create;
destructor Destroy; override;
property Foo: IFoo read FFoo implements IFoo;
end;
procedure TFooImpl.Foo;
begin
Writeln('TFooImpl.Foo called');
end;
constructor TContainer.Create;
begin
inherited;
FFoo := TFooImpl.Create;
end;
destructor TContainer.Destroy;
begin
Writeln('TContainer.Destroy called');// Эта строка никогда не выполняется
inherited;
end;
procedure Main;
var
Foo : IFoo;
begin
Foo := TContainer.Create;
Foo.Foo;
end;
begin
Main;
Readln;
end.
Если вместо использования implements реализовать интерфейс в классе TImplementor, то destructor объекта будет вызван.
Объяснение проблемы:
Проблема заключается в том, что при создании объекта TContainer и последующем присваивании ссылки на него переменной интерфейса Foo, реализующим объектом становится экземпляр TFooImpl, а не TContainer. Таким образом, ссылка на TContainer не увеличивается, и объект никогда не уничтожается.
Решение проблемы:
Для решения проблемы можно использовать класс TAggregatedObject, который позволяет агрегируемому объекту делиться жизненным циклом с агрегирующим объектом. Вот пример кода, использующего TAggregatedObject:
program SO16210993_TAggregatedObject;
{$APPTYPE CONSOLE}
type
IFoo = interface
procedure Foo;
end;
TFooImpl = class(TInterfacedObject, IFoo)
procedure Foo;
end;
TContainer = class(TInterfacedObject, IFoo)
private
FFoo: TFooImpl;
function GetFoo: IFoo;
public
destructor Destroy; override;
property Foo: IFoo read GetFoo implements IFoo;
end;
procedure TFooImpl.Foo;
begin
Writeln('TFooImpl.Foo called');
end;
destructor TContainer.Destroy;
begin
Writeln('TContainer.Destroy called');// Эта строка теперь выполняется
FFoo.Free;
inherited;
end;
function TContainer.GetFoo: IFoo;
begin
if not Assigned(FFoo) then
FFoo := TFooImpl.Create(Self);
Result := FFoo;
end;
procedure Main;
var
Foo : IFoo;
begin
Foo := TContainer.Create;
Foo.Foo;
end;
begin
Main;
Readln;
end.
Использование TAggregatedObject позволяет агрегируемому объекту FFoo делить жизненный цикл с TContainer, что и приводит к вызову destructor объекта TContainer.
Заключение:
При работе с интерфейсами в Delphi важно понимать, как работает подсчет ссылок и как он влияет на жизненный цикл объектов. В случае использования агрегирования объектов через интерфейсы, следует применять класс TAggregatedObject для корректного управления жизненным циклом объектов.
Контекст описывает проблему связанную с управлением жизненным циклом объектов в Delphi, когда при агрегировании объекта через интерфейс не происходит корректный подсчет ссылок и вызов деструктора.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.