使用Delphi的引用计数功能时内存泄漏

SOU*_*ser 7 delphi

以下代码尝试使用Delphi的引用计数功能.

但是,FullDebugMode报告中的FastMM4 DoStuff1会导致内存泄漏,而DoStuff2不会.你能帮忙评论一下为什么吗?这两个程序不应该在幕后表现完全相同吗?

program Project_SO;

{$APPTYPE CONSOLE}

uses
  FastMM4, 
  SysUtils;

type  
  ITestFunc = interface
  ['{B3F6D9A7-FC77-40CE-9BBF-C42D7037A596}']
    function DoIt(X,Y: Integer): Integer;
  end;

  TTestFunc = class(TInterfacedObject, ITestFunc)
  public
    function DoIt(X,Y: Integer): Integer;
  end;
  TTestFuncClass = class of TTestFunc;   

{ TTestFunc }

function TTestFunc.DoIt(X, Y: Integer): Integer;
begin
  Result := X + Y; 
end;

function DoStuff1(Num1, Num2: Integer; OperationClass: TTestFuncClass): Integer;
begin
  Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2);
end;

function DoStuff2(Num1, Num2: Integer; OperationClass: TTestFuncClass): Integer;
var I: ITestFunc;
begin
  I := ITestFunc(OperationClass.Create);
  Result := I.DoIt(Num1, Num2);
end;

begin
  Writeln(IntToStr(DoStuff1(3, 6, TTestFunc)));
  Writeln(IntToStr(DoStuff2(3, 6, TTestFunc)));
end.
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2);
Run Code Online (Sandbox Code Playgroud)

这里没有提到所采用的接口.将接口分配给变量或作为by值参数传递时,将引用该引用.实际上,作为by值参数传递,可以被认为在语义上等同于分配给被调用者帧中的局部变量.

但是在这段代码中没有任何地方可以参考.因此,由于没有任何参考接口,因此没有机制可以将其销毁.因此泄漏.

var 
  I: ITestFunc;
begin
  I := ITestFunc(OperationClass.Create);
  Result := I.DoIt(Num1, Num2);
end;
Run Code Online (Sandbox Code Playgroud)

在此变体中,在进行分配时进行参考.当局部变量I离开范围时,其引用计数减少到零并且实现对象被销毁.

请注意,未经检查的演员表在这里是不必要的.编译器非常清楚TTestFunc实现ITestFunc和代码应该更好地编写如下:

var 
  I: ITestFunc;
begin
  I := OperationClass.Create;
  Result := I.DoIt(Num1, Num2);
end;
Run Code Online (Sandbox Code Playgroud)

正如评论中所建议的那样,您可以删除局部变量并使用经过检查的as强制转换:

Result := (OperationClass.Create as ITestFunc).DoIt(Num1, Num2);
Run Code Online (Sandbox Code Playgroud)

执行as强制转换的结果是声明了一个隐式局部变量,接口被分配给该变量.这意味着引用计数递增到1,然后当隐式本地离开作用域时递减到零.

最后,您的TTestFunc类应该有一个虚拟构造函数,因为您打算使用元类对其进行实例化.