In my code I use a small data-storing class, which is created in different places. To avoid memory leaks and simplify things, I want to use reference counting, so I did
type TFileInfo = class (TInterfacedObject, IInterface)
and removed all my manual calls to TFileInfo.Free. Unfortunately Delphi reported a lot of memory leaks. Searching on SO I found the following question explaining why this doesn't work:
Why aren't descendants of TInterfacedObject garbage collected?
There is a workaround presented there but it requires me (at least if i get it right) to write a custom interface IFileInfo and provide it with a lot of getters and setters, which I want to avoid.
EDIT I should add that I insert the create FileInfo objects into two different kinds of hash tables: one descending from TBucketList and another one is a hash map implementation from the Codegear forum. Internally they both user pointers, so the situation is just like in the other question.
Is there any other possibility to make objects in Delphi use reference-counting?
The reference counting in Delphi only works if you only have a reference to your instance via an interface. As soon as you mix interface references and class references then you are in trouble.
Essentially you want reference counting without the need to create an interface with all the methods and properties defined in it. There are three ways to do this, and these are roughly in the order I would recommend them.
Barry Kelly wrote a post about Smart Pointers. It uses the Generics in Delphi 2009, but I am pretty sure you could hard code it to the specific versions of type you are using if you are not using 2009 yet (it is a great release BTW).
Another way that works with more versions of Delphi and less modification is the value type wrapper by Janez Atmapuri Makovsek. It is an example implemented for TStringList, but you could adapt it for any type.
The third way is to create a interfaced pointer (similar to Barry's Smart Pointer, but not so smart). I believe there is one in JCL, but I don't remember the details for sure. Basically this is an interface that accepts a TObject reference at construction. Then when it's reference count reaches zero it calls free on the object you passed to it. This method really only works for short lived instances that you are no passing as parameters because you separate the reference counted reference from the actually used reference. I would recommend one of the other two methods instead, but if you prefer this method and want more information just let me know.
That is the thing about Delphi, there are a free ways of accomplishing things. Option #1 is the best one in my opinion - Get Delphi 2009 and use that method if you can.
Good luck!
Unfortunately, the Delphi compiler generates the necessary code to inc/dec reference count only when you use interfaces (in your case custom interface IFileInfo). Moreover, if interfaces are cast to pointer (or TObject for that matter), again no reference counting is possible. For example, assumming global variable list : TList:
var ifi : IFileInfo;
begin
ifi := TFileInfo.Create;
list.Add(TFileInfo(ifi));
end;
after the method returns list[list.Count - 1] will contain dangling pointer.
So interfaces cannot be used in a hashmap that casts them to pointers, the hashmap implementation must keep them as IInterface.
Don't mix object references and interface references.
var
Intf: IInterface;
Obj: TFileInfo;
begin
// Interface Reference
Intf := TFileInfo.Create; // Intf is freed by reference counting,
// because it's an interface reference
// Object Reference
Obj := TFileInfo.Create;
Obj.Free; // Free is necessary
// Dangerous: Mixing
Obj := TFileInfo.Create;
Intf := Obj; // Intf takes over ownership and destroys Obj when nil!
Intf := nil; // reference is destroyed here, and Obj now points to garbage
Obj.Free; // this will crash (AV) as Obj is not nil, but the underlying object
// is already destroyed
end;
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