Delphi XE8. FMX Why is there a different release order for CLASS VAR on the Android platform?

Tested with Delphi XE7 Update 1 and Delphi XE8

Create an order on Windows (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 and MACOSX:

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

Android release order:

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

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

Code example:

 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. 

The 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. 
+5
source share
3 answers

Desktop compilers

Your main form is destroyed when the application object destroys its components. This 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 the proc output. This proc output is registered from TApplication.Run as follows:

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

Class constructors are called from the unit initialization section that defines them. So, TGlobalClass.Create is called from uClassVar initialization. Destructor classes are called from the completion section of the same unit.

System shutdown is performed by the System block in _Halt0 . It performs all exit procedures before finalizing the module. Therefore, your form is destroyed before the destructor class is called.

Mobile compilers

Please note that DoneApplication simply not being called on Android.

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

This means that the destruction of the main form is called from the final part of the block. Upon completion of each block, its completion sections are completed, as a result of which any global variables leave scope. In the end, there are no longer any links to your main form and therefore its destructor is executed.

As discussed above, class destructors are also called from module finalization. Since on Android, the class destructor is executed before the main form is destroyed, it is clear that uClassVar completed before the final link of the main form is released.

Now that makes sense, because uClassVar is the final unit in the initialization order and therefore the very first unit in the completion order. If you want uClassVar be completed later, you need to arrange it to initialize earlier. For example, by changing the uses clause of your .dpr file as follows:

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

Now uClassVar is the first unit initialized, and therefore the last unit is complete.

+5
source

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. 

Here, Unit2 is included as the last element in the project file, but it will not be completed first, since Unit1 uses Unit2, so the initialization order is different from the “expected” one.

The output is as follows:

 Begin Unit2.Destroy TUnit1.Destroy TUnit2.Destroy 

I'm not sure why in this case the mobile compiler will do something else ...

0
source

You use DisposeOf For Free Components

Do not use as .Free or .Destroy

Example:

 Scrollbox1.Components[1].DisposeOf; 
-1
source

All Articles