Delphi并在一个单元中完成

时间:2010-02-20 06:47:44

标签: delphi finalization

我有两个单位unitA和unitB。 类TFoo在unitB中声明。

在单元A的最终确定中调用B.Free是否总是安全?

它如何依赖于unitA和unitB在dpr中的顺序?

执行unitA终结时,我能确定unitB是否存在?

unit unitB;
interface
type
 TFoo = class
   // code...
  end;
 // code....
end;

unit unitA;
// code..
implementation
uses
 unitB;
var
 A: TStringList;
 B: UnitB.TFoo;

initialization
 A:= TStringList.Create;
 B:= UnitB.TFoo.Create;
finalization
 A.Free;
 B.Free;  // Is it safe to call?
end.

6 个答案:

答案 0 :(得分:16)

是的,你应该没问题,因为B是在单元A中创建的。规则是初始化部分是根据它们在DPR中的顺序调用的,除非其中一个单元引用另一个单元。在这种情况下,首先初始化引用的单元。最终确定的顺序相反。

在你的情况下,单元B没有初始化部分,所以这是一个没有实际意义的点。但是,当执行单元A初始化部分时,它将使用单元B中的TFoo定义。

关于Initialization和Finalization部分的另一个警告 - 它们发生在全局异常处理程序之外。发生的任何异常都会终止应用程序。因此,跟踪和调试这些异常可能是大型程序的痛苦。您可以考虑在那里使用自己的异常日志记录,以确保。

答案 1 :(得分:4)

NO。您可以尝试,但您可以通过 无保证 来调用初始化和终结。请参阅qc72245qc56034many more

<强>更新

  1. 完成部分的执行顺序与初始化相反。您的示例是安全的,您不依赖于调用单元之间的初始化部分
  2. Delphi可以混合调用单元(第1点仍然有效,初始化和终结部分都是交换的)
  3. 示例:

    unitA // no dependency on unitB
    var SomeController;
    initialization
      SomeController := TSomeController.Create;
    finalization
      SomeController.Free;
    
    unitB
    uses
      unitA;
    initialization
      SomeController.AddComponent(UnitBClass);
    finalization
      SomeController.RemoveComponent(UnitBClass);
    

    调用的通用(正确)顺序(99.99%):

    1. unitA.initialization
    2. unitB.initialization
    3. 运行...
    4. unitB.finalization
    5. unitA.finalization
    6. 但是有时候可以使Delphi编译文件错误:

      1. unitB.initialization - AV here
      2. unitA.initialization
      3. 运行...
      4. unitA.finalization
      5. unitB.finalization - 此处也是
      6. 小小的脱离故事:

        我们有一个相当大的项目,Unit1中的Type1,Unit2中的Type2 = class(Type1)。文件在project.dpr中排序,并在几年后添加Unit200(与unit1 / 2 无依赖关系)Delphi开始在Unit1.Initialization之前使用Unit2.Initialization编译项目。只有安全的解决方案是从初始化部分调用您自己的Init函数。

答案 2 :(得分:3)

据我所知,你所拥有的应该是完全有效的。有点尴尬但有效。

但更好的方法可能是在单元B中声明一个变量并让B初始化/最终化它。 由于初始化在任何其他代码被调用之前发生,因此只要它在单元A的uses子句中声明,它就会在单元A可用之前进行初始化。

您可能需要考虑的另一个步骤是将B的单位变量更进一步,并将其作为按需加载的函数调用,但这也可能取决于您的使用情况。

例如

unit unitB;
interface
type
 TFoo = class
   // code...
  end;
 // code....
 function UnitVarB:TFoo;

implementation

var
  gUnitVarB : TFoo;  

function UnitVarB:TFoo 
begin
  if not assigned(gUnitVarB) then
    gUnitVarB := TFoo.Create;

  result := gUnitVarB;
end;

finalization
  if assigned(gUnitVarB) then
    gUnitVarB.free;  //or FreeAndNil(gUnitVarB);

end;

unit unitA;

// code..
implementation

uses
 unitB;

var
 A: TStringList;

//code...
  ...UnitVarB....
//code...

initialization
 A:= TStringList.Create;
finalization
 A.Free;
end.

我似乎记得单位初始化可能是昂贵的,因为如果你不再直接引用的单元在编译期间仍然在你的uses子句中,智能链接器将不会因为初始化部分而删除它。虽然如果每个单元都有一个初始化部分,这可能听起来不那么糟糕,那么大多数Delphi程序将比它们已经更大 MUCH

我不是说不要使用它们,但我的经验法则是谨慎使用它们。

您的初始代码示例违反了该规则。我以为我会提到它。

赖安

答案 3 :(得分:3)

在这里展示的具体情况中,你会没事的。但在开始出错之前,不需要那么多的重构。

Delphi做得很好,确保只要需要,单位就会留在内存中。但只有在知道需要一个单位的情况下才能这样做。

关于这个主题的经典例子是一个只包含对象列表的单元

unit Unit1;

interface
uses
  Contnrs;
var
  FList : TObjectList;
implementation

initialization
  FList := TObjectList.Create(True);
finalization
  FList.Free;
end.

Unit1仅明确依赖于Contnrs。 Delphi只会确保Contnrs单元(可能还有“subdependant”单元,虽然我不是100%肯定)仍然在内存中加载。如果在列表中添加了TForm,那么在调用FList.free时,Forms单元可能已经完成,当它试图释放它包含的TForm时它将崩溃。 Delphi无法知道Unit1需要Forms单元。在这种特定情况下,它将取决于在dpr中声明的订单单位。

答案 4 :(得分:1)

是的,这是安全的。您可以通过在dpr文件中的UnitA之前声明UnitB来简化编译器的工作,但编译器将在任何情况下解析引用。

答案 5 :(得分:1)

本着完全公开的精神,我自2005年以来就没有在Delphi中开发过。但是,我在Delphi中开发的专用于1996年的Delphi 1,并于2001年在Delphi 5中获得认证。话虽如此,我使用的是最终确定部分很少见。我唯一一次使用它是因为我需要在.dpr中设置一些特殊的东西。这通常只发生在我进行自定义组件开发时,并且我需要使用我正在开发的其他自定义组件来管理一些依赖项。

对于典型的应用程序开发,我远离初始化/完成部分,只使用单例,外观和工厂等设计模式来管理我的类的创建和管理。内置的垃圾收集器足以满足98.5%的项目需求。

要回答您的问题,您需要在UnitA代码中设置TFoo的依赖关系,并且正如Ryan建议的那样,确保在销毁之前分配它。话虽如此,我建议您在投入太多时间之前确保使用初始化/完成部分。