5

Tested with Delphi XE7 Update 1 and Delphi XE8

Create order on Windows OS (7 SP1 x64), MACOSX (10.10.3) and Android (5.0.2):

    "class constructor TGlobalClass.Create;" -> "constructor TfmMain.Create;" -> "procedure TfmMain.FormCreate(Sender: TObject);"

Release order on Windows OS and MACOSX:

    "TfmMain.FormDestroy" -> "destructor TfmMain.Destroy" -> "class destructor TGlobalClass.Destroy;"

Release order on Android:

    "class destructor TGlobalClass.Destroy;" -> "TfmMain.FormDestroy" -> "destructor TfmMain.Destroy"

Question is: why on Android platform CLASS VAR releasing before main form?

Sample of code:

unit uClassVar;

interface

type
  TGlobalClass = class
    class var F1: Integer;

    class constructor Create;
    class destructor Destroy;
  end;

implementation

{ TX }

class constructor TGlobalClass.Create;
begin
  { Breakpoint there }
  F1 := 100;
end;

class destructor TGlobalClass.Destroy;
begin
  { Breakpoint there }
  F1 := 200;
end;

end.

Main unit:

unit ufmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics;

type
  TfmMain = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  fmMain: TfmMain;
  z: Integer;

implementation

uses
  uClassVar;

{$R *.fmx}

constructor TfmMain.Create;
begin
  { Breakpoint there }
  inherited;
end;

destructor TfmMain.Destroy;
begin
  { Breakpoint there }
  inherited;
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  { Breakpoint there }
  TGlobalClass.F1 := -99999;
end;

procedure TfmMain.FormDestroy(Sender: TObject);
begin
  { Breakpoint there }
  z := 200;
end;

end.

Project file:

program ClassVar;

uses
  System.StartUpCopy,
  FMX.Forms,
  ufmMain in 'ufmMain.pas' {fmMain},
  uClassVar in 'uClassVar.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TfmMain, fmMain);
  Application.Run;
end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Zam
  • 2,880
  • 1
  • 18
  • 33

3 Answers3

5

Desktop compilers

Your main form is destroyed when the application object destroys its components. That happens in FMX.Forms in the DoneApplication procedure.

procedure DoneApplication;
begin
  if Screen <> nil then
    Screen.ActiveForm := nil;
  Application.DestroyComponents;  <-- this is destroying your main form
end;

And DoneApplication is called during shutdown as an exit proc. That exit proc is registered from TApplication.Run like this:

{$IFNDEF ANDROID}
  AddExitProc(DoneApplication);
{$ENDIF}

Class constructors are called from the initialization section of the unit which defines them. So, TGlobalClass.Create is called from the initialization of uClassVar. Class destructors are called from the finalization section of that same unit.

The system shutdown is performed by the System unit in _Halt0. It executes all the exit procs before performing unit finalization. Hence your form is destroyed before the class destructors are called.

Mobile compilers

Note that DoneApplication is simply not called on Android.

{$IFNDEF ANDROID}
  AddExitProc(DoneApplication);
{$ENDIF}

This means is that the main form's destruction is being invoked from unit finalization. As each unit is finalized, its finalization sections are executed which result in any global variables leaving scope. Eventually, there are no more references to your main form and so its destructor is executed.

As discussed above, the class destructors are also called from unit finalization. Since on Android, your class destructor executes before the main form is destroyed, it is clear to see that uClassVar is finalized before the main form's final reference is released.

Now, that makes perfect sense because uClassVar is the final unit in the initialization order, and hence the very first unit in the finalization order. If you wanted to ensure that uClassVar is finalized later, you need to arrange for it to be initialized sooner. For instance, by changing the uses clause of your .dpr file like so:

uses
  uClassVar in 'uClassVar.pas',  
  System.StartUpCopy,
  FMX.Forms,
  ufmMain in 'ufmMain.pas' {fmMain};

Now uClassVar is the first unit initialized, and hence the last unit finalized.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Yes. Because on iOS `DoneApplication` is registered as an exit proc. – David Heffernan Jun 26 '15 at 11:29
  • "Class constructors are called from the initialization section" more correct statement would be Class ctors are called _before_ initialization section executes, and class dtors are called _after_ finalization section is executed. – Dalija Prasnikar Jun 26 '15 at 13:27
  • @Dalija I was writing from the perspective of the startup/shutdown code in System. Which makes no distinction. The ordering of the unit's implicit initialization wrt to its explicit initialization isn't critical here. – David Heffernan Jun 26 '15 at 13:32
  • I know it makes no difference in this case, but being more precise might help future readers in some different context. – Dalija Prasnikar Jun 26 '15 at 13:37
  • @Dalija I suppose it depends on your perspective. Is it not reasonable to regard class constructors as part of init/finit? – David Heffernan Jun 26 '15 at 13:39
  • @Dalija The docs say: *Calls to class constructors are inserted automatically by the compiler into the initialization section of the unit where the class is defined.* By that text, my words are accurate. – David Heffernan Jun 26 '15 at 13:41
  • Docs are imprecise. In some cases it may be relevant that class constructors will run before initialization, or destructors after finalization section. [Allen Bauer: Class Constructors](http://blogs.embarcadero.com/abauer/2009/09/04/38899) – Dalija Prasnikar Jun 26 '15 at 14:04
  • 1
    @DalijaPrasnikar For sure it will sometimes matter. But I think you are making more of this than is really relevant to the question at hand. If we are in the business of considering asides, it is interesting to note that globals vars (including class vars) are finalised before class destructors execute. Which in my view renders class constructors/destructors of limited utility. – David Heffernan Jun 26 '15 at 14:12
0

The program:

program Destructors;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Unit1 in 'Unit1.pas',
  Unit2 in 'Unit2.pas';

var
  X: TUnit1;
begin
  x := TUnit1.Create;
  x.Free;
  Writeln('Begin');
end.

Unit1:

unit Unit1;

interface

uses
  System.Classes, Unit2;

type
  TUnit1 = class
  public class var
    X: TUnit2;
  public
    class constructor Create;
    class destructor Destroy;
    destructor Destroy; override;
  end;

implementation

{ TUnit1 }

class constructor TUnit1.Create;
begin
  X := TUnit2.Create;
end;

class destructor TUnit1.Destroy;
begin
  X.Free;
  Writeln('TUnit1.Destroy');
end;

destructor TUnit1.Destroy;
begin
  Writeln('Unit1.Destroy');
  inherited;
end;

end.

Unit2:

unit Unit2;

interface

uses
  System.Classes;

type
  TUnit2 = class
  public class var
    X: TComponent;
  public
    class constructor Create;
    class destructor Destroy;
    destructor Destroy; override;
  end;

implementation

{ TUnit2 }

class constructor TUnit2.Create;
begin
  X := TComponent.Create(nil);
  X.Name := ClassName;
end;

class destructor TUnit2.Destroy;
begin
  X.Free;
  Writeln('TUnit2.Destroy');
end;

destructor TUnit2.Destroy;
begin
  Writeln('Unit2.Destroy');
  inherited;
end;

end.

There Unit2 is included as the last unit in the project file, but it will be not finalized first as Unit1 uses the Unit2 - so initialization order is different from the "expected".

The output is following:

Begin
Unit2.Destroy
TUnit1.Destroy
TUnit2.Destroy

I am not sure why in such case the mobile compiler would do something different...

Z.B.
  • 1,185
  • 9
  • 18
  • @David Heffernan: Or is that different on the mobile platforms? – Z.B. Jun 26 '15 at 10:55
  • Did you read my answer closely enough? The point being that on **all** platforms other than Android, the main form is destroyed **before** unit finalization commences. Did you observe the point that DoneApplication is not called from finalization, but is called from an exit proc. And did you note that exit procs are called before any unit finalizations. And did you note the explicit {$IFNDEF ANDROID} which means that DoneApplication is not registered as an exit proc? – David Heffernan Jun 26 '15 at 11:52
  • My fault, @DavidHeffernan. I just checked that issue in detail. – Z.B. Jun 26 '15 at 11:56
  • 1
    Your edit fails to grasp the point of the question. The asker is not asking about unit finalization order per se. The asker is concerned with ordering of main form destruction with respect to unit finalization. What you have seemingly not grasped is that for all targets other than Android, main form destruction is not triggered from unit finalization. – David Heffernan Jun 26 '15 at 12:28
-1

You are using DisposeOf For Free Components

Don't use as .Free or .Destroy

Example:

Scrollbox1.Components[1].DisposeOf;  
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
asdfavfwer
  • 11
  • 4