【问题标题】:Where should a class free its private variables?一个类应该在哪里释放它的私有变量?
【发布时间】:2011-01-11 18:21:36
【问题描述】:

由于Delphi没有垃圾回收,你具体在哪里卸载变量?

假设我有一个带有一组私有变量的类型。 有一个可以做到这一点的 Destroy 方法就足够了吗? 我需要在我的消费类中显式调用这个destroy方法吗?

【问题讨论】:

    标签: delphi memory-management garbage-collection


    【解决方案1】:

    在 delphi 中组织破坏的最佳方式是始终考虑“谁将创建给定的变量”。

    如果您也在这种情况下释放它们(为您私有变量类的销毁方法),那么您遇到内存泄漏的可能性要小得多。

    其实类的析构函数通常不会被调用

    myInstance.Destroy();
    

    典型的做法是通过

    FreeAndNil(myInstance);
    

    myInstance.Free();
    

    因为delphi会按顺序处理调用析构方法

    【讨论】:

    • +1 如果你遵循“谁创造谁毁灭”,那么你已经 98% 在那里了。
    • 但是你能走多远。例如,如果我的表单破坏了我的模型类,我的模型类是否仍然需要释放它自己的所有变量?我现在的雇主告诉我表单的 FormDestroy() 中的以下行就足够了: fTheInstance.Destroy;我唯一拥有非 gb-collected 的 XP 是 Cobol 和 Oberon,我的直觉告诉我这条线是不够的..
    • 关于销毁,摘自“Delphi in a nutshell”:oreilly.com/catalog/delphi/chapter/ch02.html ...释放对象时,您应该调用 Free 方法而不是析构函数。区别很重要,因为 Free 检查对象引用是否为 nil,并且仅对非 nil 引用调用 Destroy。在特殊情况下,一个类可以重新定义 Free 方法……这使得调用 Free 而不是 Destroy 变得更加重要。如果你只是在你的主对象上调用destroy而不明确地销毁childrne,你可能会遇到内存泄漏
    【解决方案2】:

    当您准备好使用对象时,需要销毁它们:使用 .Free(调用析构函数 .Destroy)或 FreeAndNil。

    如果您使用接口:它们是引用计数的,因此它们会为您释放。字符串也是如此。

    您需要传递对象的大多数函数都不会获得该对象的所有权,因此您应该在完成后释放它。如果您在表单上创建组件,请注意它们是对象,属于表单,您应该让表单为您释放它们。

    例如:在您的 TLight 中,您创建的 TTimer 可能没有所有者。在这种情况下,您应该在 TLight 类的析构函数中释放计时器。如果您的 TLight 是 TControl 或 TComponent 本身,您可以创建 Timer,并将 Self 作为 Owner,并在您的 TLight 实例被释放时自动释放。

    【讨论】:

    • 重新所有权 - 请注意,这不是编译器魔法,而只是隐藏在 VCL 中的普通旧 Delphi 代码。
    • @Ulrich:是的。我认为所有权对于来自垃圾收集语言的人来说是一项复杂的壮举:TList 对其对象没有所有权,TObjectList 有,TComponents 有一个简单的所有权构建,但是当控件的父级(也是一个组件)被破坏时,它将即使 Owner 设置为不同的组件,也要销毁它的子组件。 Delphi、C、C++ 中的许多错误源于所有权问题,甚至困扰着经验丰富的程序员。
    • 根据我的经验,接口是一个特例。如果一个类具有接口类型的属性,我发现有必要将 nil 分配给析构函数中该属性的私有字段。销毁对象不会减少引用计数器。也许我错了,但我会再测试一次:)
    • Mjustin,如果您使用的是接口,您甚至不应该拥有任何对象引用供您销毁。如果你有一个接口属性,那么你应该有一个接口字段,而不是一个对象字段。最终析构函数运行后(在 FreeInstance 中),接口字段将自动清除。
    • @mjustin:应该不需要将引用计数项设置为零(字符串、接口实例、动态数组)。实例的完成已经为您完成了。
    【解决方案3】:

    我是这么看的:

    原始类型(如整数、实数、双精度、扩展)、数组、集合、记录:超出范围时会自动销毁。

    引用计数类型(字符串、接口类型、接口引用):当它们的引用计数器为零时,它们会自动销毁。

    属于这些情况之一的类中的对象:不从 TComponent 派生,在不实现所有权的列表对象中,或者是 TComponent 的派生对象但没有分配所有者

    • 当他们的类从 TInterfacesObject 继承并从接口引用访问时:引用计数器类型的行为相同(见上文)
    • 其他情况:调用 Object.Free 或 FreeAndNil(Object) 手动销毁它(在 try..finally 结构中会更好)

    拥有所有者的对象(TComponent 后代或在实现所有权的列表对象中):所有者将在适当的时候取走它们。

    我注意到 OP 的以下评论:

    我注意到了所有者的概念。 但是我无法将自定义课程传递给 TTimer.Create() 方法原样 期待一个 TComponent。为什么他们 限制在表单中使用的东西 只要?这不是进一步石化 将逻辑放入你的坏习惯 视图(表单)?

    看,TComponent 的后代可以在托盘上注册,以便能够在设计时使用(例如,在表单中)。但这并不意味着它必须! 您可以将自定义类传递给 TTimer.Create() 如果它来自 TComponent 但您不需要在设计时实例化它 - 甚至由 TForm 后代拥有(如果您想管理它的生命周期,您甚至不需要所有者!)。

    所有权是一种帮助人们节省时间的机制......

    【讨论】:

    • 谢谢,这是我可以理解的语言 :)
    • 希望我可以标记两个答案,因为这个答案说什么时候销毁什么,另一个说怎么做
    【解决方案4】:

    您一直提到“带有私有变量的类型”并询问“卸载变量”。我将冒险出去(因为我可能误解了你的信息),并假设你在谈论类变量,并询问当“类卸载”时这些类变量应该何时完成。 (您在某个时候一直在进行 Java 或 .NET 编程,没有 ;-)。

    您要问的关键是单元定稿部分。一个单元的整体布局包括4个整体部分,其中两个是可选的,初始化和终结。

    unit Foo;
    interface
      // put things here you want other units to see
    implementation
      // put the *implementation* of the things from the interface here along with things
      // you only want this unit to see.
    initialization
      // <optional> put code here that you want to execute when this unit is "loaded"
    finalization
      // <optional> put code here that you want to execute when this unit is "unloaded"
    end.
    

    如果您谈论的是类类型的 instance 变量,那么您已经获得(并且一定会获得)的其他一些答案应该会对此有所了解。

    【讨论】:

    • 谢谢。虽然这不是我真正想要的,但这个简单的概述清除了一些仍然悬而未决的迷雾。是的,我来自 C# bgnd,但尝试不同的语言来“磨刀”永远不会有坏处,对吧? ;) 我喜欢 init 和 finit 部分。我想我可能会使用它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 2015-01-04
    • 1970-01-01
    • 2011-05-26
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    相关资源
    最近更新 更多