Memory leak when using Delphi reference counting function

The following code attempts to use the link counting function for Delphi.

However, FastMM4 in FullDebugMode reports DoStuff1 gives a memory leak, but DoStuff2 does not. Could you explain why? Shouldn't these two procedures behave exactly the same behind the stage?

 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. 
+7
delphi
source share
1 answer
 Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2); 

Nowhere is there a link to the accepted interface. The link is made when the interface is assigned to a variable or passed as a parameter by value. In fact, passing as a parameter by value, can be considered semantically equivalent to assigning a local variable in the call frame.

But nowhere in this code is a link used. So, since nothing refers to an interface, there is no mechanism for destroying it. Hence the leak.

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

In this embodiment, reference is made upon appointment. When the local variable I leaves the scope, its reference count is reduced to zero and the implementation object is destroyed.

Please note that duty free reception is not needed here. The compiler knows very well that TTestFunc implements ITestFunc , and it is better to write your code like this:

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

As pointed out in the comments, you can remove the local variable and use the marked as cast:

 Result := (OperationClass.Create as ITestFunc).DoIt(Num1, Num2); 

A consequence of the implementation of the as cast is that an implicit local variable is declared to which the interface is assigned. This means that the reference count is incremented by one, and then reduced to zero when this implicit local area of โ€‹โ€‹the sheet.

Finally, your TTestFunc class must have a virtual constructor, since you intend to create it using a metaclass.

+5
source share

All Articles