Подтвердить что ты не робот

Почему реализация интерфейса основана на утечке памяти TComponent?

Этот код 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?

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

4b9b3361

Ответ 1

Различия в реализации

  • 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;

Ответ 2

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

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

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

Ответ 3

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

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