Delphi: С каких это пор ссылки на интерфейсы больше не выпускаются в конце блока with?
Недавно я наткнулся на проблему, вызванную некоторым очень старым кодом, который я написал, который, очевидно, предполагал, что ссылки на интерфейсы, используемые в with
заявление будет выпущено, как только with
-блок остался - вроде как неявный try-finally
-блок (аналогично C#) using
заявление, если я правильно понял).
По-видимому (в 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
это злой аргумент, это всегда был единственный пример, который я имел в виду, который заставлял меня говорить "Да, но...". Кажется, я был неправ... Кто-нибудь может подтвердить?
2 ответа
with
сохраненное слово в Pascal/Delphi используется только для легкого доступа к членам записей или объектов / классов (т. е. чтобы не упоминать имя записи / объекта / класса). Это очень отличается от C# with
что касается сбора мусора. Он существует на языке Паскаль со дня records
родились, чтобы упростить вызов кода для многих членов данных (тогда они назывались просто "полями").
Подвести итоги, 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;