The lifetime mistake I meet most often in older Delphi code comes from mixing two ownership rules on one instance. A class that descends from TInterfacedObject is freed when its interface reference count reaches zero. A variable of the class type is not counted at all. Hold the same instance through both kinds of reference and the two rules disagree about when it dies.
program Greeter;
{$APPTYPE CONSOLE}
type
IGreeter = interface
procedure Hello;
end;
TGreeter = class(TInterfacedObject, IGreeter)
procedure Hello;
end;
procedure TGreeter.Hello;
begin
WriteLn('hello');
end;
procedure Use(G: IGreeter);
begin
G.Hello;
end;
var
Obj: TGreeter;
begin
Obj := TGreeter.Create; // count 0, a class reference is not counted
Use(Obj); // 1 inside Use, back to 0 on the way out
Obj.Free; // the object is already gone
end.
The instance starts with a count of zero, because the class reference never touched it. Passing it to Use converts it to IGreeter, the count goes to one for the duration of the call and back to zero on the way out, and at zero the object destroys itself. The Obj.Free that follows is a second destruction of memory that has already been released. The compiler accepts all of it.
My rule is that once a class implements interfaces through TInterfacedObject, every long-lived reference to an instance is an interface reference, and the class type appears only in the line that calls the constructor. I am curious whether others draw the line in the same place.
