[英]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部分的另一个警告 - 它们发生在全局异常处理程序之外。 发生的任何异常都会终止应用程序。 因此,跟踪和调试这些异常可能是大型程序的痛苦。 您可以考虑在那里使用自己的异常日志记录,以确保。
没有。 您可以尝试,您可以希望但是无法保证按顺序调用初始化和完成。 请参阅qc72245 , qc56034 等等 。
更新:
例:
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%):
但有时 Delphi编译文件错误:
小小的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.