对象的引用计数
在我的代码中,我使用一个小的数据存储类,它是在不同的地方创建的。 为了避免内存泄漏并简化事情,我想使用引用计数,因此我这样做
type TFileInfo = class (TInterfacedObject, IInterface)
并删除了对 TFileInfo.Free 的所有手动调用。 不幸的是,Delphi 报告了很多内存泄漏。 搜索SO我发现以下问题解释了为什么这不起作用:
为什么TInterfacedObject 的后代不会被垃圾收集吗?
这里有一个解决方法,但它需要我(至少如果我做对的话)编写一个自定义接口 IFileInfo 并为其提供大量的 getter 和 setter,我需要这样做想要避免。
编辑 我应该补充一点,我将创建的 FileInfo 对象插入到两种不同类型的哈希表中:一种是从 TBucketList 降序的,另一种是来自 Codegear 论坛的哈希映射实现。 在内部,它们都是用户指针,所以情况就像另一个问题一样。
是否还有其他可能使 Delphi 中的对象使用引用计数?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
仅当您仅通过接口引用实例时,Delphi 中的引用计数才有效。 一旦你混合了接口引用和类引用,你就会遇到麻烦。
本质上,您需要引用计数,而不需要创建一个包含其中定义的所有方法和属性的接口。 有三种方法可以做到这一点,这些方法大致按照我推荐的顺序排列。
Barry Kelly 写了一篇关于智能指针的文章。 它使用 Delphi 2009 中的泛型,但我非常确定,如果您尚未使用 2009,您可以将其硬编码为您正在使用的类型的特定版本(顺便说一句,这是一个很棒的版本)。
另一种适用于更多版本的 Delphi 且修改较少的方法是 值类型包装亚内兹·阿特马普里·马科夫塞克。 这是为 TStringList 实现的示例,但您可以将其改编为任何类型。
第三种方法是创建一个接口指针(类似于 Barry 的智能指针,但没那么智能)。 我相信 JCL 里有一个,但具体细节我不太记得了。 基本上,这是一个在构造时接受 TObject 引用的接口。 然后,当它的引用计数达到零时,它会对您传递给它的对象调用 free 。 此方法实际上仅适用于您不作为参数传递的短期实例,因为您将引用计数的引用与实际使用的引用分开。 我会推荐其他两种方法之一,但如果您更喜欢这种方法并想要更多信息,请告诉我。
这就是 Delphi 的特点,有一种免费的方式来完成事情。 我认为选项#1 是最好的 - 获取 Delphi 2009 并使用该方法(如果可以的话)。
祝你好运!
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!
不幸的是,只有当您使用接口(在您的情况下是自定义接口 IFileInfo)时,Delphi 编译器才会生成必要的代码来增加/减少引用计数。 此外,如果接口被强制转换为指针(或 TObject),则同样不可能进行引用计数。 例如,假设全局变量 list : TList:
在方法返回 list[list.Count - 1] 后将包含悬空指针。
因此,接口不能在将它们转换为指针的哈希图中使用,哈希图实现必须将它们保留为 IInterface。
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:
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.
此功能是为接口提供的,但不是为对象提供的。
您可以创建类似的东西,但您需要重写 TObject 的一些结构:
您需要 RefCountedCopy 将对象分配给另一个变量。 但是这样你就有了一个重新计数的对象。
如何使用这个:
This functionality is supplied for interfaces but not for objects.
You can create something like it, but you need to override some of the structure of TObject:
You need RefCountedCopy to assign the object to another variable. But then you have a refcounted object.
How to use this:
如果您想消除对 TObject 实例上的 free 调用,那么您可能需要查看本机 Delphi 的垃圾收集器。 我知道两种不同的垃圾收集器和垃圾收集技术,每种都有优点和缺点。
本机精确跟踪垃圾收集器,作者:Henrick Hellström
Boehm 垃圾收集器 DLL 的 API,作者:Barry Kelly
垃圾收集器Delphi 对象和组件 作者:Rossen Assenov(不是真正的垃圾收集器,更像是一个内存袋。)
其中之一可能适合您。
If you want to eliminate calls to free on TObject instances then you might want to look at a garbage collector for native Delphi. I am aware of 2 different garbage collectors and a garbage collecting technique, each with pros and cons.
Native Precise Tracking Garbage Collector by Henrick Hellström
API for Boehm Garbage Collector DLL by Barry Kelly
Garbage Collector for Delphi Objects and Components by Rossen Assenov (not a real garbage collector, more of a memory bag.)
One of those will probably work for you.
补充一下已经说过的内容,如果您想存储对接口的引用,请使用 TInterfaceList,而不是使用 TList。 引用计数将始终如一地工作。
To add to what has already been said, if you want to store references to Interfaces, instead of using a TList, use a TInterfaceList. The ref count will work consistently.
对此有一个很长的解释,但简而言之:从 TInterfacedObject 继承(而不是自己调用 Free)是不够的,您需要使用对象工厂动态来为您创建对象,并使用指向对象无处不在,而不仅仅是对象引用变量。 (是的,这意味着你不能不检查就直接切换“旧代码”)
There's a long explanation to this, but in short: Inheriting from TInterfacedObject (and not calling Free yourself), is not enough, you need to use an object-factory-dynamic to create the objects for you, and use interface-pointers to the object everywhere, not just object-reference-variables. (Yes, that means you can't just switch 'old code' without looking it over)