The following code try to utilize the reference counting feature of Delphi.
However, FastMM4 in FullDebugMode reports DoStuff1
gives memory leak while DoStuff2
does not. Could you help to comment about why ? Shouldn't these two procedures behave exactly the same behind the scene ?
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.
1 System.ReportMemoryLeaksOnShutdown. In latest Delphi versions 2006 or later we can set ReportMemoryLeaksOnShutdown = True before Application.Run code in Project file to report memory leaks on shutdown. 2 FASTMM. FastMM is actually the memory manager included with Delphi since a few years. ... 3 Memcheck. ... 4 MadExcept. ... 5 Eurekalog. ...
The reason for that is that Delphi's automatic reference counting only works with interfaces. With variables declared as interfaces, automatic reference counting will be handled by the compiler, and you will not have to put in any deallocation calls in order to prevent memory leaks.
All Delphi versions since Delphi 2006 have an updated memory manager that is faster and more feature rich. One of the nicest features of the "new" memory manager allows applications to register (and unregister) expected memory leaks, and optionally report unexpected memory leaks on program shutdown.
MemCheck hunts memory leaks, memory corruption, use of an object after its destroying, method calls on interface references which refer to a destroyed object, etc. MemCheck is freeware, with source. 4. MadExcept madExcept was built to help you locating crashes in your Delphi application.
Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2);
Nowhere here is a reference to the interface taken. A reference is taken when the interface is assigned to a variable, or passed as a by value parameter. In fact, passing as a by value parameter, can be thought of as semantically equivalent to assigning to a local variable in the callee's frame.
But nowhere in this code is a reference taken. And so, since nothing has a reference to the interface, there is no mechanism for it to be destroyed. Hence the leak.
var
I: ITestFunc;
begin
I := ITestFunc(OperationClass.Create);
Result := I.DoIt(Num1, Num2);
end;
In this variant, a reference is taken when the assignment is made. When the local variable I
leaves scope, its reference count decreases to zero and the implementing object is destroyed.
Note that the unchecked cast is needless here. The compiler knows perfectly well that TTestFunc
implements ITestFunc
and your code should better be written like this:
var
I: ITestFunc;
begin
I := OperationClass.Create;
Result := I.DoIt(Num1, Num2);
end;
As suggested in the comments, you could remove the local variable and use a checked 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. That means that the reference count is incremented to one, and then decremented to zero when that implicit local leaves scope.
Finally, your TTestFunc
class should have a virtual constructor since you intend to instantiate it with a meta class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With