суббота, января 31, 2009

Утечки на интерфейсных параметрах

Теряются объекты передаваемые в качестве параметров интерфейсных типов в случае, когда не происходит кастинга с проверкой (as) к определенному интерфейсу. Для кода:

 Test(TMyObject.Create('1')); // will not be destroyed
Test(IInterface(TMyObject.Create('2'))); // will not be destroyed
Test(TMyObject.Create('3') As IInterface);

утечки будут в первых двух вызовах. Воспроизводится, и на Turbo Delphi, и на Delphi 2009. Отчет #71015.

10 комментариев:

Анонимный комментирует...

Не могу повторить, а в поле Attachment на QC обычный текст. Не могли бы вы выолжить полный текст примера прямо тут? Он не должен быть большим.

Kazantsev Alexey комментирует...

Проверил QC, там все нормально (проверял через стандартного QC-клиента).

Код:
program intfs;

{$APPTYPE CONSOLE}

Type

TMyObject = Class(TInterfacedObject)

FName : String;

Constructor Create(Const AName : String);
Destructor Destroy; Override;

End;

{ TMyObject }

constructor TMyObject.Create(const AName: String);
begin

Inherited Create;

FName := AName;

end;

destructor TMyObject.Destroy;
begin

inherited;

Writeln('Object ', FName, ' is destroyed');

end;

//
Procedure Test(Const AIntf : IInterface);
Begin
End;
//

begin

Test(TMyObject.Create('1')); // will not be destroyed
Test(IInterface(TMyObject.Create('2'))); // will not be destroyed
Test(TMyObject.Create('3') As IInterface);

//
// Output:
//
// Object 3 is destroyed;
//

end.

Анонимный комментирует...

По-моему это as designed.
Если мы передаем в процедуру объект, то с какого перепуга этот объект должен самостоятельно уничтожиться?
Предположим, что "баг" исправлен. Что тогда будет с кодом:

First := TMyObject.Create('4');
Second := First;
Test(First);

при условии, что Second - глобальная переменная (TMyObject), которую мы будем использовать уже после выхода First за пределы видимости? Скорее всего будет AV.

P.S. Работать надо или с объектами или с интерфейсами. Мешать не стоит.

Kazantsev Alexey комментирует...

>Если мы передаем в процедуру
>объект, то с какого перепуга этот
>объект должен самостоятельно
>уничтожиться?

С какого перепугу мы передаем объект? В параметре, что объектный тип используется?

Анонимный комментирует...

Конструктор возвращает объект. В метод с параметром типа byte можно передать переменную типа integer, но снаружи вызываемого метода она останется integer.
Если не хочется "странного" поведения, то при создании объекта нужно присваивать его интерфейсной переменной и работать через нее, а про тип объекта забыть.

P.S. На QC заходил через браузер (Опера, IE7).

Kazantsev Alexey комментирует...

>Конструктор возвращает объект.
>В метод с параметром типа byte
>можно передать переменную типа
>integer, но снаружи вызываемого
>метода она останется integer.

Не нужно передергивать. Byte, Integеr и иже с ними не являются типами с управляемым временем жизни, в отличие от интерфейсных типов.

>Если не хочется "странного"
>поведения, то при создании
>объекта нужно присваивать его
>интерфейсной переменной и
>работать через нее, а про тип
>объекта забыть.

Мне не требуется ни каких переменных. Я вполне осознанно отдаю объект, зная, что он будет приведен к интерфейсному типу и потому о времени его жизни не беспокоюсь. А то, что наблюдается есть чистейший баг.

P.S. На QC заходил через браузер (Опера, IE7).

Глюки QC. Через клиент все работает нормально.

Анонимный комментирует...

>Не нужно передергивать. Byte, >Integеr и иже с ними не являются >типами с управляемым временем >жизни, в отличие от интерфейсных >типов.

Ну хорошо, скажу по другому:

В процедуру с параметром типа string (управляемый тип, в вашем случае интерфейс) можно передать переменную типа char или, например, string[10] (неуправляемые типы, в вашем случае объект).
Тут конечно ситуация слегка разнится, потому, что строка в памяти будет скопирована, но внешнее поведение аналогично - после передачи char'а в процедуру я могу с ним спокойно работать и не опасаться что его память освобождена.
Вы же предлагаете, чтобы после передачи объекта в процедуру, требующую интерфейс, этот объект помирал (поскольку интерфейсных ссылок на него больше нет), и воспользоваться им после этого нельзя было.

Kazantsev Alexey комментирует...

>В процедуру с параметром типа
>string (управляемый тип, в вашем
>случае интерфейс) можно передать
>переменную типа char или,
>например, string[10]

Еще раз: не нужно передергивать. Любые аналогии здесь не уместны.

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

Это не я предлагаю, это правило языка.

Анонимный комментирует...

Причина - в спецификаторе const формального параметра -
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=609

Kazantsev Alexey комментирует...

Неприятная особенность