Функции, возвращающие интерфейсы
Есть простой тестовый код:
program intftest;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
ITest = Interface
['{F76722CE-6B62-49FE-8D5E-8646558DE528}']
function GetRefCount: Integer;
end;
TTest = class(TInterfacedObject, ITest);
function GetTest: ITest;
begin
Result := TTest.Create;
WriteLn(Result.GetRefCount); // 1
end;
var
Test: ITest;
begin
try
Test := GetTest;
WriteLn(Test.GetRefCount); // 2
Test := nil;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
Вопрос: что я делаю не так, почему по выходу из функции GetTest() значение Test.RefCount = 2? И как в этом случае корректно уничтожить объект?..
Ответы (1 шт):
Как правильно отметили ранее, если поместить код программы внутрь другого метода, тогда всё будет работать ожидаемо:
program intftest;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
ITest = Interface
['{F76722CE-6B62-49FE-8D5E-8646558DE528}']
function GetRefCount: Integer;
end;
TTest = class(TInterfacedObject, ITest);
function GetTest: ITest;
begin
Result := TTest.Create;
WriteLn(Result.GetRefCount); // 1
end;
procedure Test;
var
Test: ITest;
begin
Test := GetTest;
WriteLn(Test.GetRefCount); // 1
Test := nil;
end;
begin
try
Test;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
При этом если сделать GetTest ЛОКАЛЬНОЙ ФУНКЦИЕЙ процедуры Test, то мы снова получим предыдущее поведение:
program intftest;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
ITest = Interface
['{F76722CE-6B62-49FE-8D5E-8646558DE528}']
function GetRefCount: Integer;
end;
TTest = class(TInterfacedObject, ITest);
procedure Test;
function GetTest: ITest;
begin
Result := TTest.Create;
WriteLn(Result.GetRefCount); // 1
end;
var
Test: ITest;
begin
Test := GetTest;
WriteLn(Test.GetRefCount); // 2
Test := nil;
end;
begin
try
Test;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
При этом количество ссылок доходит до нуля и объект освобождается В МОМЕНТ ЗАВЕРШЕНИЯ процедуры Test.
Из всего этого я делаю вывод, что для всех результатов выполнения локальных функций создаётся скрытая переменная, которая освобождается в конце выполнения кода, для которого эти функции являются локальными.
Но в консольной программе все процедуры и функции являются для основного Begin/end локальными, и поэтому Ваша переменная освободится в самом конце.
Поэтому при написании консольных приложений у Вас два выхода:
- В основном
Begin/endвызывать только основную процедуру (напримерRun), для которой остальные функции не будут являться локальными (если Вы этого не захотите), а значит и не будет создаваться скрытая переменная. И на мой взгляд, это правильно. - Использовать процедуры с
outпараметром.