Ok, let's make an overview. Uses objpash.inc.
We have classes, that have a virtual method table (record TVmt), static methods and class methods, like:
class function NewInstance: TObject; virtual;
If you call the constructor, NewInstance is called in the background.
The difference between class methods and other methods is, that the latter get an invisible first parameter, Self, that points to the instance. While class methods are regular functions. NewInstance is just a function that returns a TObject.
After the TObject, we see the definition of IUnknown. This basically extends your classes with two new functions: _AddRef and _Release, which are used for reference counting.
The basic class that implements both, is TInterfacedObject. The main difference with a TObject is, that it adds a property RefCount and overrides NewInstance and Destroy, to add to or substract from that reference count.
Why does it do that and how does that help us with the memory management?
How it actually works isn't explained in this source file. To understand that, we have to read between the lines and fill in the blanks that are hidden inside the compiler. Like. how it calls NewInstance automatically. Every time you assign an instance to a variable, the compiler calls _AddRef (if available). And every time you assign something else to that variable, or it goes out of scope, it calls _Release. See:
function TObject.GetInterface(const iid : tguid; out obj) : boolean;
(For the implementations, see 'function fpc_intf*' in compproc.inc. It's hard to show the code where this is used, because the compiler uses offsets into the VMT instead of function names to call the _AddRef and _Release methods. To see it happen, use the debugger.)
The result of all that is, that if there are no variables anymore that reference an instance, it is deleted.
Or, in other words: instead of inheriting from class(TObject), use class(TInterfacedObject) to create your own ones and call the inherited destructor as usual. Like this:
type
TMyClass = class(TInterfacedObject)
MyVar: string;
constructor Create(ThisVar: string); virtual;
destructor Destroy; override;
end;
constructor TMyClass.Create(ThisVar: string);
begin
inherited Create;
MyVar := ThisVar;
end;
destructor TMyClass.Destroy;
begin
inherited Destroy;
end;
var
m, n: TMyClass;
begin
m := TMyClass.Create('Bla');
n := m;
m := TMyClass.Create('Zomg!');
// Do stuff
// Don't Free them!
end;
That should be enough.
But, there's a lot of existing classes you didn't write yourself, where that doesn't work. What do you do?
Well, the easiest way is probably to inherit a new class, that adds and implements an IUnknown interface, like TInterfacedObject does. Or use a smart pointer. The most interesting one is probably
this one as posted earlier, which should now work on trunk.