Delphi: Desde quando são referências de interface não lançado no final de um bloco com?
-
22-07-2019 - |
Pergunta
Recentemente, tropeçou em um problema causado por algum código muito antigo que eu escrevi que foi, obviamente, assumindo que as referências interface utilizados em um comunicado with
seria liberado tão logo o with
-block é deixado - como uma espécie de try-finally
-block implícita (semelhante ao using
-statement 's C # se eu entendi corretamente).
Aparentemente (em Delphi 2009), este não é (não?) O caso. Alguém sabe quando isso aconteceu? Ou era o meu código apenas errado liso para começar?
Para esclarecer, aqui está um exemplo simplificado:
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
Sempre que alguém começou o velho "with
é mal" argumento este era sempre o exemplo que eu tinha em mente o que me manteve "Sim, mas ...". Parece que eu estava errado ... Alguém pode confirmar?
Solução
A palavra preservada with
em Pascal / Delphi só é usado para acessar facilmente os membros de registros ou objetos / classes (ou seja, a fim de não mencionar o nome de do registro / objeto da classe /). É muito diferente do # with
C que se relaciona com a coleta de lixo. Tem existido na linguagem Pascal desde o records
dia nasceram, para simplificar o código chamando a muitos membros de dados (na época chamado simplesmente de "campos").
Para resumir, with
não tem nada a ver com a coleta de lixo, liberação de memória ou destruição de objeto instâncias. Objetos que são construídos no cabeçalho with
apenas poderia ter sido inicializado em uma linha de código separado antes, é o mesmo.
Outras dicas
Este COM-comportamento nunca mudou. Para chegar ao seu comportamento esperado pode alterar o código da seguinte maneira:
procedure TestIt;
var
myIntf: IMyIntf;
begin
DoSomething;
myIntf := TSomeObject.GetMyIntf
DoStuff;
DoMoreStuff;
myIntf := nil; // <- here is where TSomeObject.Destroy called
DoSomethingElse;
end;
ou você pode fazê-lo no procedimento:
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;