tags:

views:

441

answers:

8

What techniques exist to automatically free objects in delphi applications?

+11  A: 

Use interfaces instead of objects. They are reference counted and freed automatically when the reference count reaches 0.

Glenner003
+1 for this answer. But be very very careful when mixing objects and interfaces. And make sure that you don't create circular references between interfaces, as they will then keep each other alive, giving you essentially memory leaks. Example would be a interface with children, which each keep ...
mghie
a reference to their parent. There are ways around this (typecasting), but better to design differently in the first place.
mghie
A reminder: The implementation of the interface does the actual reference counting and can decide not to use it. Like TComponent does. So using interfaces does not always mean the objects get destroyed automaticly.
Lars Truijens
True. I do however question whether that is good design practice. It's understandable in the context of the VCL, but for own design it seems a bad idea to burden the consumer of the interface with the ownership problem. Better stick with the idiom, there's always a way to implement it (adapters).
mghie
+4  A: 

Use the object ownership of components that the VCL provides. As long as you create objects with a non-nil owner you don't need to free them explicitely. See also my answer to this question.

mghie
A: 

If you use Delphi for .Net / Delphi Prism you get Garbage Collection which takes care of all the freeing.

Lars Truijens
+1  A: 

Smart Pointers work really well if you have Delphi 2009.

Jim McKeeth
This falls under use of interfaces, though.
mghie
Still worth mentioning as it is a specific implementation which happens to use interfaces. Its easier for some to grasp smart pointers and not have to refactor everything to use interfaces directly.
skamradt
Agreed. Still, some of the gotchas of interfaces (like that cyclic references must be avoided) apply in much the same way for smart pointers.
mghie
+4  A: 

I have to say, I don't like "hiding" the Free of an object. Far better to have the traditional code:

MyObject := TObject.Create;
try
  // do stuff
finally
  FreeAndNil(MyObject);
end;

No way it can go wrong, works as expected, and people recognise the pattern.

mj2008
If you don't like "hiding" the Free of an object, then why have you hidden it behind the use of FreeAndNil when you could have called Free directly?
Rob Kennedy
After FreeAndNil() MyObject = nil and when you try access to MyObject after that then you get Access Violation immediately. There is no need to call FreeAndNil() instead Free() when (for example) your MyObject isn't global object and is used only by one thread.
inzKulozik
Technically I agree with inzKulozik, but I still use FreeAndNil everywhere I free a object as a good practice. Even if your object is a local variable and it's not being used after the FreeAndNil, who knows when someone might add some more code to your method which does use it afterwards.
Ben Daniel
I agree entirely with Ben. First, it is an easy habit to use, and code gets added.
mj2008
+2  A: 

Along the lines of interfaces, you can try the Guard function in the JclSysUtils unit, part of the free Jedi Code Library. It allows you to associate an object with a separate interface reference, so when that interface reference is destroyed, the object is destroyed along with it. This can be useful when you don't have the option of modifying the classes you're using to make them support interfaces of their own.

var
  G: ISafeGuard;
  foo: TStrings;
begin
  // Guard returns TObject, so a type-cast is necessary
  foo := Guard(TStringList.Create, G) as TStrings;
  // Use the object as normal
  foo.Add('bar');
end; // foo gets freed automatically as G goes out of scope

There are overloads for objects and GetMem-allocated pointers. There is also IMultiSafeGuard, which can ensure that multiple objects get freed.

If you have a factory function, you might be creating an object, setting some of its properties, and then returning it. If an exception occurs while setting the properties, you'll want to make sure you free the object since you can't return it. One way to do that is like this:

function Slurp(const source: TFileName): TStrings;
begin
  Result := TStringList.Create;
  try
    Result.LoadFromFile(source);
  except
    Result.Free;
    raise;
  end;
end;

With Guard, it would become this:

function Slurp(const source: TFileName): TStrings;
var
  G: ISafeGuard;
begin
  Result := Guard(TStringList.Create, G) as TStrings;
  Result.LoadFromFile(source);
  G.ReleaseItem;
end;

The ReleaseItem method revokes the ISafeGuard's ownership of the object. If an exception occurs before that happens, then as the stack unwinds and the interface is released, the guard will free the object.

Rob Kennedy
Perferct! I Used to Create a Descendent Class of the target one, then inject IInterface type. Your way is the very clear and best way. Thaks.
Gedean Dias
+2  A: 

I have written a function GC(obj: TObject) (for Garbage Collect) which takes an object and frees it when the execution leaves the current method. It's kind of like a one-line shorthand function for a Try Finally Free block.

Instead of:

procedure Test;
var AQuery: TQuery;
begin
  AQuery := TQuery.Create(nil);
  try
    ...
  finally
    FreeAndNil(AQuery);
  end;
end;

I just have:

procedure Test;
var AQuery: TQuery;
begin
  AQuery := TQuery.Create(nil);
  GC(AQuery);
  ...
end;

The GC function simply returns an object in the form of an interface.

function GC(obj: TObject): IGarbo;
begin
  Result := TGarbo.Create(obj);
end;

Because the TGarbo class descends from TInterfacedObject, when the TGarbo object goes out of scope it will automatically get freed. In the destructor of the TGarbo object, it also frees the object you passed to it in it's constructor (the object you passed in the GC function).

type
  IGarbo = interface
    ['{A6E17957-C233-4433-BCBD-3B53C0C2C596}']
    function Obj: TObject;
  end;

  TGarbo = class(TInterfacedObject, IGarbo)
  private
    FObj: TObject;
  public
    constructor Create(AObjectToGC: TObject);
    destructor Destroy; override;
    function Obj: TObject;
  end;

{ TGarbo }

constructor TGarbo.Create(AObjectToGC: TObject);
begin
  inherited Create;
  FObj := AObjectToGC;
end;

destructor TGarbo.Destroy;
begin
  if Assigned(FObj) then
    FreeAndNil(FObj);
  inherited;
end;

function TGarbo.Obj: TObject;
begin
  Result := FObj;
end;

Being stuck in the world of Delphi 7 with no sight of upgrading to a version of Delphi with built-in garbage collection in the near future, I'm addicted to using this short-hand method of easily freeing local temporary objects! :)

Ben Daniel
+1  A: 

Here is the API for Boehm Garbage Collector DLL for Delphi. The Delphi API is written by Barry Kelly, who works for CodeGear writing the compiler now.

Jim McKeeth
It doesn't seems work together with FASTMM4
Gedean Dias