Try this: Create a procedure that contains the single line
V := AdoConnection1.Execute(Sql).Fields[0].Value;
, put a breakpoint on it run the app and view the disassembly. You'll see that just before the line
jmp @HandleFinally
there are three calls to
call @IntfClear
That's the compiler releasing the three interfaces it has had to access in order to execute the statement, namely
- the RecordSet interface returned by AdoConnection1.Execute(),
- the Fields interface of that RecordSet, and
- the particular Field interface obtained via Fields[0].
So, it has automatically generated the code necessary to free up these interfaces after executing the source statement.
The following is an imperfect analogy but its disassembly is much easier to follow; it illustrates the code the compiler automatically generates to deal with finalizing interfaces.
Given
type
IMyInterface = interface
function GetValue : Integer;
end;
TMyClass = class(TInterfacedObject, IMyInterface)
function GetValue : Integer;
destructor Destroy; override;
end;
TForm1 = class(TForm)
[...]
procedure Button1Click(Sender: TObject);
end;
destructor TMyClass.Destroy;
begin
inherited;
end;
function TMyClass.GetValue: Integer;
begin
Result := 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I : IMyInterface;
begin
I := TMyClass.Create;
Caption := IntToStr(I.GetValue);
end;
the CPU disassembly of Button1Click looks like this

and the line arrowed red is where the interface is cleared despite the source code
not doing anything explicit to do this. Put a breakpoint on the
inherited
in TMyClass.Destroy and you'll find that also gets called, again despite the
source code not explicitly calling it.
Like I said, the above is an imperfect analogy. An interesting thing is that for the horrific (from the pov of the usage of the "with" construct) alternative
procedure TForm1.Button1Click(Sender: TObject);
begin
with IMyInterface(TMyClass.Create) do
Caption := IntToStr(GetValue);
end;
which uses no (explicit) local variable, the compiler generates the exact same code as the disassembly illustrated.
Of course, the situation in the q is slightly different because the memory allocated to the recordset object is on the other side of the Ado COM interface, so one has no control over whether that memory is correctly de-allocated beyond the fact that the compiler will generate the code to call _Release on the interface to it.