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