Почему реализации интерфейса основаны на утечке памяти TComponent? - PullRequest
18 голосов
/ 02 февраля 2010

Этот код Delphi покажет утечку памяти для экземпляра TMyImplementation:

program LeakTest;

uses
  Classes;

type
  MyInterface = interface
  end;

  TMyImplementation = class(TComponent, MyInterface)
  end;

  TMyContainer = class(TObject)
  private
    FInt: MyInterface;
  public
    property Impl: MyInterface read FInt write FInt;
  end;

var
  C: TMyContainer;
begin
  ReportMemoryLeaksOnShutdown := True;

  C := TMyContainer.Create;
  C.Impl := TMyImplementation.Create(nil);
  C.Free;
end.

Если заменить TComponent на TInterfacedObject и конструктор изменился на Create (), утечка исчезнет. Что здесь отличается от TComponent?

Большое спасибо за ответы. Подводя итог: легко, но неправильно сказать: «Если вы используете интерфейсы, они подсчитывают ссылки и, следовательно, освобождаются для вас». - Фактически любой класс, который реализует интерфейс, может нарушить это правило. (И никаких подсказок или предупреждений компилятора не будет.)

Ответы [ 3 ]

28 голосов
/ 02 февраля 2010

Отличия в реализации

  • TComponent._Release не освобождает ваш экземпляр.
  • TInterfacedObject._Release освобождает ваш экземпляр.

Возможно, кто-то может подключиться, но я считаю, что TComponent не предназначен для использования в качестве объекта с подсчетом ссылок, как мы обычно используем интерфейсы.

Реализация TComponent._Release

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;
20 голосов
/ 02 февраля 2010

TComponent не реализует свои методы _AddRef и _Release так же, как TInterfacedObject. Он откладывает подсчет ссылок до своего свойства VCLComObject , которое должно быть некоторым другим интерфейсным объектом. Поскольку TComponent не считает ссылки, он не может определить, когда его счетчик достигает нуля, поэтому он не освобождает себя.

Свойство VCLComObject содержит ссылку на интерфейс, которая должна реализовывать IVCLComObject . Если связанному с компонентом объекту VCLComObject сказано, что он владеет компонентом, то, когда счетчик ссылок этого интерфейса достигнет нуля, он уничтожит связанный с ним компонент. Говорят, что он владеет компонентом, вызывая его метод FreeOnRelease.

Все это разработано, чтобы упростить упаковку компонентов VCL в COM-объекты. Если это не ваша цель, то вы, вероятно, столкнетесь с некоторыми другими неожиданными аспектами проектирования на этом пути, так что вы можете пересмотреть мотивацию для того, чтобы ваши компоненты в первую очередь реализовывали интерфейсы.

6 голосов
/ 02 февраля 2010

Компонент должен принадлежать и уничтожаться чем-то другим, обычно формой. В этом случае счетчик ссылок не используется. Если вы передадите компонент в качестве ссылки на интерфейс, было бы очень прискорбно, если бы он был уничтожен при возврате метода.

Поэтому подсчет ссылок в TComponent был удален.

...