我有两个单位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.
答案 0 :(得分:16)
是的,你应该没问题,因为B是在单元A中创建的。规则是初始化部分是根据它们在DPR中的顺序调用的,除非其中一个单元引用另一个单元。在这种情况下,首先初始化引用的单元。最终确定的顺序相反。
在你的情况下,单元B没有初始化部分,所以这是一个没有实际意义的点。但是,当执行单元A初始化部分时,它将使用单元B中的TFoo定义。
关于Initialization和Finalization部分的另一个警告 - 它们发生在全局异常处理程序之外。发生的任何异常都会终止应用程序。因此,跟踪和调试这些异常可能是大型程序的痛苦。您可以考虑在那里使用自己的异常日志记录,以确保。
答案 1 :(得分:4)
NO。您可以尝试,但您可以通过 无保证 来调用初始化和终结。请参阅qc72245,qc56034和many more。
<强>更新强>
示例:
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编译文件错误:
小小的脱离故事:
我们有一个相当大的项目,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建议的那样,确保在销毁之前分配它。话虽如此,我建议您在投入太多时间之前确保使用初始化/完成部分。