Delphi: С каких это пор ссылки на интерфейсы больше не выпускаются в конце блока with?
-
22-07-2019 - |
Вопрос
Недавно я наткнулся на проблему, вызванную каким-то очень старым кодом, который я написал, который, очевидно, предполагал, что ссылки на интерфейсы, используемые в операторе with
, будут освобождены, как только с
-блок слева - вроде как неявный try-finally
-блок (аналогично с использованием
-статации в C #, если я правильно понял).
По-видимому (в Delphi 2009) это не так (уже не так). Кто-нибудь знает, когда это произошло? Или мой код был просто неправильным для начала?
Чтобы пояснить, вот упрощенный пример:
type
IMyIntf = interface;
TSomeObject = class(TInterfacedObject, IMyIntf)
protected
constructor Create; override; // creates some sort of context
destructor Destroy; override; // cleans up the context created in Create
public
class function GetMyIntf: IMyIntf; //a factory method, calling the constructor
end;
procedure TestIt;
begin
DoSomething;
with (TSomeObject.GetMyIntf) do
begin
DoStuff;
DoMoreStuff;
end; // <- expected: TSomeObject gets destroyed because its ref.count is decreased to 0
DoSomethingElse;
end; // <- this is where TSomeObject.Destroy actually gets called
Всякий раз, когда кто-то начинал, старый " с
- это зло " аргумент, это всегда был единственный пример, который я имел в виду, который поддерживал меня "Да, но ...". Похоже, я был неправ ... Кто-нибудь может подтвердить?
Решение
Сохраненное слово with
в Pascal / Delphi используется только для легкого доступа к членам записей или объектов / классов (то есть, чтобы не упоминать имя записи / объекта / класса). Это очень отличается от C # с
, что касается сборки мусора. Он существует на языке Pascal со дня рождения records
, чтобы упростить вызов кода для многих членов данных (тогда это просто называлось " fields "). Р>
Подводя итог, можно сказать, что with
не имеет ничего общего с сборкой мусора, освобождением памяти или уничтожением экземпляров объектов. Объекты, которые создаются в заголовке with
, могли бы просто быть инициализированы в отдельной строке кода раньше, это то же самое. Р>
Другие советы
Это WITH-поведение никогда не менялось. Чтобы достичь ожидаемого поведения, вы можете изменить свой код следующим образом:
procedure TestIt;
var
myIntf: IMyIntf;
begin
DoSomething;
myIntf := TSomeObject.GetMyIntf
DoStuff;
DoMoreStuff;
myIntf := nil; // <- here is where TSomeObject.Destroy called
DoSomethingElse;
end;
или вы можете сделать это в процедуре:
procedure TestIt;
procedure DoAllStuff;
var
myIntf: IMyIntf;
begin
myIntf := TSomeObject.GetMyIntf
DoStuff;
DoMoreStuff;
end; // <- here is where TSomeObject.Destroy called
begin
DoSomething;
DoAllStuff;
DoSomethingElse;
end;