views:

550

answers:

5

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.
+2  A: 

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

Ryan J. Mills
Thanks. Function UnitVarB is clever way to avoid global variable.
pKarelian
I use CnPack's uses cleaner. It seems to skip units that have initialization.
pKarelian
+1  A: 

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.

Serg
+1  A: 

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.

Neil T.
Delphi does not have a garbage collector.
Jim McKeeth
The poster is most likely referring to the VCL "Owner" behaviour and/or the fact that memory for objects that are not explicitly Free'd is returned to the system when the process terminates, although of course their destructors do not run.
Deltics
Jim: Thanks for reminding me...apparently, the exorcism was a complete success. :o)Deltics: I remember having to use FreeAndNil often to release the memory and the pointer in my code for objects I created in code, but I wouldn't use the initialization section unless I was working with a system-managed resource, like an COM object. Even then, it was typically associated with dependent libraries or making sure resources were available when my component started.
Neil T.
+7  A: 

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.

Jim McKeeth
voted up for the 'global exception handler' note
frogb
See my comment. Delhi doesn't guarantee order. Your exception note is true.
DiGi
It's worth to note that Madexcept catches those exceptions raised in finalization sections.
utku_karatas
+1  A: 

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.

DiGi
The original question is not about the initialization/finalization order.
Serg
Even if unit B is finalized first, that doesn't cause the unit to stop *existing*. Code that uses things defined in unit B will continue to work.
Rob Kennedy
But it depends. **finalization** parts **are** executed in reverse order to initialization, but Delphi can swap units and order
DiGi