繁体   English   中英

Delphi并在一个单元中完成

[英]Delphi and finalization in a unit

我有两个单位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.

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

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

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

没有。 您可以尝试,您可以希望但是无法保证按顺序调用初始化和完成。 请参阅qc72245qc56034 等等

更新:

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

例:

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

有时 Delphi编译文件错误:

  1. unitB.initialization - AV在这里
  2. unitA.initialization
  3. 跑...
  4. unitA.finalization
  5. unitB.finalization - 也在这里

小小的offtopic故事:

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

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

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

您可能要考虑的另一个步骤是将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子句中,智能链接器将不会因为初始化部分而删除它。 虽然这听起来可能不是那么糟糕,如果每个单位有一个初始化部分则最德尔福计划将远大于他们已经是。

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

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

瑞安

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

德尔福做得非常好,确保只要需要,单位就会留在内存中。 但只有知道需要一个单位才能这样做。

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

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被调用时,它会尝试释放它包含的TForm的它将会崩溃。 Delphi无法知道Unit1需要Forms单元。 在这种特定情况下,它将取决于在dpr中声明的订单单位。

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM