Неиспользуемая ссылка на интерфейс не уничтожается - PullRequest
7 голосов
/ 13 января 2011

В моем приложении была еще одна ошибка, вызванная небрежным использованием интерфейсов Delphi. Когда я передаю интерфейс процедуре, которая игнорирует этот аргумент, экземпляр никогда не освобождается. Смотрите следующий простой пример:

ITest = interface
    procedure Test;
end;

Tester = class(TInterfacedObject, ITest)
public
    procedure Test;
end;

Base = class
public
    procedure UseTestOrNot(test : ITest); virtual; abstract;
end;

A = class(Base)
public
    procedure UseTestOrNot(test : ITest); override;
end;

B = class(Base)
public
    procedure UseTestOrNot(test : ITest); override;
end;

{ A }

procedure A.UseTestOrNot(test: ITest);
begin
    test.Test();
end;

{ B }

procedure B.UseTestOrNot(test: ITest);
begin
    WriteLn('No test here');
end;

// -------- Test ---------------------------------------
var
    list : TObjectList<Base>;
    x : Base;
    t : ITest;
begin
    ReportMemoryLeaksOnShutdown := true;

    list := TObjectList<Base>.Create;
    list.Add(A.Create);
    list.Add(B.Create);

    // 1 x Tester leak for each B in list:
    for x in list do
        x.UseTestOrNot(Tester.Create);

    // this is ok
    for x in list do
    begin
        t := Tester.Create;
        x.UseTestOrNot(t);
    end;

    list.Free;
end.

Не могли бы вы объяснить, что не так со счетчиком ссылок? Можете ли вы дать какие-либо рекомендации / рекомендации (например, «Никогда не создавайте интерфейсный экземпляр внутри вызова функции [если вы не знаете, что происходит внутри]).

Лучшее решение, которое я могу придумать для этого примера, - написать шаблонный метод в классе Base, который сохраняет пройденный тестовый экземпляр и вызывает абстрактный метод DoUseTestOrNot.

EDIT Delphi 2010

Ответы [ 2 ]

8 голосов
/ 13 января 2011

Это другое проявление ошибок здесь .
Я добавлю это в отчет о КК.

Это больше не воспроизводится в обновлении Delphi XE 1.

- Йерун

8 голосов
/ 13 января 2011

Добавьте руководство к вашей декларации ITest

ITest = interface
['{DB6637F9-FAD3-4765-9EC1-0A374AAC7469}']
    procedure Test;
end;

Измените цикл на этот

for x in list do
    x.UseTestOrNot(Tester.Create as ITest);

GUID необходим для использования as

Test.Create as ITest заставляет компилятор добавить релиз, в котором созданный объект выходит из области видимости.

...