Delphi 和单元中的最终确定

发布于 2024-08-22 10:30:32 字数 471 浏览 4 评论 0原文

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

在unitA的最终确定中调用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.

I have two units unitA and unitB.
Class TFoo is declared in unitB.

Is it allways safe to call B.Free in finalization of unitA?

How does it depend on in which order unitA and unitB are in dpr?

Can I be sure that unitB exists when unitA finalization is executed?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

橘寄 2024-08-29 10:30:32

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

在您的情况下,单元 B 没有初始化部分,因此它是一个有争议的点。然而,当执行单元 A 初始化部分时,它将使用单元 B 中的 TFoo 定义。

关于初始化和终结部分的另一句话警告 - 它们发生在全局异常处理程序之外。那里发生的任何异常都会终止应用程序。因此,在大型程序中跟踪和调试这些异常可能会很痛苦。为了确定起见,您可以考虑使用自己的异常日志记录。

Yes, you should be fine since B is created in Unit A. The rule is that the Initialization sections are called based on the order they are in the DPR, unless one of the units references another unit. In that case, the referenced unit is initialized first. Finalization is in the reverse order.

In your case Unit B does not have an initialization section, so it is a moot point. It will however use the TFoo definition in Unit B when the Unit A initialization section is executed.

Another word of warning about Initialization and Finalization sections - they happen outside of the global exception handler. Any exception that occurs there just terminates the application. So tracking down and debugging those exceptions can be a pain in large programs. You might consider using your own exception logging in there, just to be sure.

乜一 2024-08-29 10:30:32

不。您可以尝试,您可以希望,但是不能保证调用初始化和完成的顺序。请参阅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.initializationunitB.initializationrun
  2. unitB.initialization
  3. ...
  4. unitB.finalizationunitA.finalization
  5. 但是

有时Delphi编译文件可能会出错:

  1. - AV这里
  2. unitA.initializationrun
  3. ...
  4. unitA.finalizationunitB
  5. 。最终确定 - 这里也是

一个小题外话:

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

NO. You can try, you can hope but there is no guarantee in order of calling initialization and finalization. See qc72245, qc56034 and many more.

UPDATE:

  1. finalization section is executed in reverse order than initialization. Your example is safe, you don't have dependency on calling initialization sections between units
  2. Delphi can mix calling units (point 1 is still valid, both initialization and finalization sections are swapped)

Example:

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);

Common (correct) order (99.99%) of calling:

  1. unitA.initialization
  2. unitB.initialization
  3. run...
  4. unitB.finalization
  5. unitA.finalization

But sometimes can Delphi compile file wrong:

  1. unitB.initialization - AV here
  2. unitA.initialization
  3. run...
  4. unitA.finalization
  5. unitB.finalization - and here too

Little offtopic story:

We have quite big project, with Type1 in Unit1, Type2 = class(Type1) in Unit2. Files are ordered in project.dpr and after years and adding Unit200 (no dependency with unit1/2) Delphi starts compiling project with Unit2.Initialization before Unit1.Initialization. Only safe solution is calling your own Init functions from initialization section.

如何视而不见 2024-08-29 10:30:32

据我了解,你所得到的应该是完全有效的。有点尴尬但有效。

但更好的方法可能是在单元 B 中声明一个变量并让 B 初始化/终结它。
由于初始化发生在调用任何其他代码之前,因此只要在单元 A 的 use 子句中声明它,它就会在单元 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 程序将比现在大得多。

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

您最初的代码示例违反了该规则。我想我会提一下。

瑞安

As far as I understand it what you've got should be perfectly valid. A little awkward but valid.

But a better way might be to declare a variable in Unit B and have B initialize/finalize it.
Since initializations happen before any other code is called it will be initialize before it is made available to Unit A as long as it is declared in the uses clause of Unit A.

One other step you might want to consider is taking the unit variable of B one step further and have it as a function call for on demand loading, but that might also be dependant on your usage.

for example

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.

I seem to remember somewhere that unit initializations could be expensive in that if a unit that you no longer directly reference is still in your uses clause during a compile, the smart linker will not remove it because of the initialization section. While this may not sound that bad if every unit had an initialization section then most Delphi programs would be MUCH bigger than they already are.

I'm not saying don't use them but my rule of thumb is to use them sparingly.

Your initial code example breaks that rule. I thought I'd mention it.

Ryan

尐偏执 2024-08-29 10:30:32

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

Delphi 在确保单元在需要时保留在内存中方面做得非常好。但只有当它知道需要一个单元时它才能这样做。

我关于该主题的经典示例是一个只包含对象列表的单元,

unit Unit1;

interface
uses
  Contnrs;
var
  FList : TObjectList;
implementation

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

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

In the specific situation you are showing here, you will be alright. But it wouldn't take that much refactoring before it starts going wrong.

Delphi does a pretty good job making sure units stays in memory as long as they are needed. But it can only do so if it knows a unit is needed.

My classical example on the topic is a unit containing nothing but an objectlist

unit Unit1;

interface
uses
  Contnrs;
var
  FList : TObjectList;
implementation

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

Unit1 is only explicitly dependant on Contnrs. Delphi will only ensure the Contnrs unit (and probably also "subdependant" units, though I'm not 100% sure) is still loaded in memory. If a TForm is added in the list, the Forms unit might be already finalized when FList.free is called it will crash when it tries to free the TForm it contains. Delphi has no way to know Unit1 requires the Forms unit. In this specific case, it will depend on the order units are declared in the dpr.

回首观望 2024-08-29 10:30:32

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

Yes, that is safe. You can simplify the compiler's job by declaring UnitB prior to UnitA in dpr file, but the compiler will resolve the references in any case.

情愿 2024-08-29 10:30:32

本着充分披露的精神,自 2005 年以来我就不再使用 Delphi 进行开发。但是,我从 1996 年开始使用 Delphi 1 专门使用 Delphi 进行开发,并于 2001 年在 Delphi 5 中获得认证。话虽这么说,我对最终化部分的使用是罕见的。我唯一会使用它的是如果我需要在 .dpr 中设置一些特殊的东西。这种情况通常只有在我进行自定义组件开发时才会发生,并且我需要使用我正在开发的其他自定义组件来管理一些依赖项。

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

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

In the spirit of full disclosure, I haven't developed in Delphi since 2005. However, I developed in Delphi exclusively starting with Delphi 1 in 1996, and was certified in Delphi 5 in 2001. That being said, my use of the finalization section was rare. The only time I would use it is if I needed to set up something special in the .dpr. That typically only occurred if I was doing custom component development, AND there were some dependencies that I needed to manage using other custom components I was developing.

For typical application development, I stayed away from the initialization/finalization section and just used design patterns like singletons, facades and factories to manage the creation and management of my classes. The built-in garbage collector was good enough for 98.5% of my projects.

To answer your question, you need to set up a dependency on TFoo in your UnitA code, and, as Ryan suggested, make sure it's assigned prior to destruction. That being said, I encourage you to make sure that the use of the initialization/finalization section is necessary before you invest too much time with it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文