Can I throw around class
es without freeing them, or will my software start spouting leaks?
For example can I do this
Engine := TEngine.Create(TV);
Then get rid of the reference without any problems, or must I call its Free
method first?
Or have a function that returns a TSomething
and not having to free its reference later on?
The general rule is that if you create it, you should free it. The best way is in a try..finally if you're creating it in code:
var
Engine: TEngine;
begin
Engine := TEngine.Create(TV);
try
// Do stuff with Engine
finally
Engine.Free;
end;
end;
The exception to this is if you have an object that accepts an owner as a parameter (such as a visual control like TEdit
or a non-visual descendant of TComponent
). If you assign the owner, it will free it when the owner is freed. (If you create it without an owner, you still have to free it yourself.)
procedure TForm1.FormCreate(Sender: TObject);
var
EditA, EditB: TEdit;
begin
EditA := TEdit.Create(Self); // You're passing the form as owner; don't free
EditB := TEdit.Create(nil); // Creating without an owner; you free.
end;
If the class is a member (field) of another object, you create it in the containing objects constructor
and free it in its destructor
:
type
TOuterClass = class(TObject)
private
FEngine: TEngine;
public
constructor Create;
destructor Destroy; override;
end;
implementation
constructor TOuterClass.Create;
begin
inherited;
FEngine := TEngine.Create(TV);
end;
destructor TOuterClass.Destroy;
begin
FEngine.Free;
inherited;
end;
Technically yes, everything that you initialize with a constructor must be freed explicitly.
But there are some easy workarounds that when used properly, can save you most of the trouble:
1: Use TInterfacedObject:
IMyStuff = interface(IUnknown)
['{9DF82155-2475-4403-8933-969DC4912AD7}']
function Print:boolean;
procedure DoStuff;
end;
TMyStuff = class(TInterfacedObject, IMyStuff)
private
function Print:boolean;
procedure DoStuff;
end;
Implement TMyStuff just like any other class. But when you USE that class in your code, use a variable of type IMyStuff, like this:
procedure MyIProcedure;
var myStuff: IMyStuff;
begin
myStuff:=TMyStuff.create;
myStuff.DoStuff;
end;
No need to cast in the 'TMyStuff.create' call (in this case - sometimes it is...) - since the variable is type IMystuff this is implicit. No need to free IMyStuff ( In fact you can't, although you can call IMyStuff:=nil.) Since it is declared as an interface type, automated garbage collection is implemented using the the COM reference counting model - Delphi handles this for you when you inherit from TInterfacedObject.
BUT do not mix class type variables: i.e. TMyStuff with IMyStuff variable types. This can lead to some nasty confusion and errors that will leave you scratching your head. That is the reason I generally declare members of TInterfacedObject as PRIVATE, as I did here, making them in inaccessible via references to TMyStuff. However they are accesible via a reference to IMyStuff: all interface members are public, by definition.
2: Besides Ken's excellent answer, the VCL's TObjectList and TObjectDictionary provide automated garbage collection for all the object references they contain (see Delphi docs for details) - but they themselves must be freed, and then the rest gets freed with them.
Use TObjectList or TObjectDictionary in a TInterfacedObject and never worry about garbage collection again, as long as you free the ObjectList or ObjectDictionary in the destructor of your TInterfacedObject. That destructor will be called automatically by Delph's implementation when referenceCount=0.