DelphiXE8。调音台。为什么 CLASS VAR 在 Android 平台上的发布顺序不同?
Delphi XE8. FMX. Why is different release order of CLASS VAR on Android platform?
使用 Delphi XE7 Update 1 和 Delphi XE8
进行测试
在 Windows OS (7 SP1 x64)、MACOSX (10.10.3) 和 Android (5.0.2) 上创建订单:
"class constructor TGlobalClass.Create;" -> "constructor TfmMain.Create;" -> "procedure TfmMain.FormCreate(Sender: TObject);"
在 Windows OS 和 MACOSX:
发布订单
"TfmMain.FormDestroy" -> "destructor TfmMain.Destroy" -> "class destructor TGlobalClass.Destroy;"
Android 发布订单:
"class destructor TGlobalClass.Destroy;" -> "TfmMain.FormDestroy" -> "destructor TfmMain.Destroy"
问题是:为什么在 Android 平台上 CLASS VAR 在主窗体之前发布?
代码示例:
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.
主机:
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.
项目文件:
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.
桌面编译器
当应用程序对象销毁其组件时,您的主窗体也被销毁。这发生在 DoneApplication
过程的 FMX.Forms
中。
procedure DoneApplication;
begin
if Screen <> nil then
Screen.ActiveForm := nil;
Application.DestroyComponents; <-- this is destroying your main form
end;
并且 DoneApplication
在关闭期间作为退出过程被调用。该退出过程是从 TApplication.Run
注册的,如下所示:
{$IFNDEF ANDROID}
AddExitProc(DoneApplication);
{$ENDIF}
Class 构造函数从定义它们的单元的初始化部分调用。因此,TGlobalClass.Create
是从 uClassVar
的初始化调用的。 Class 从同一单元的终结部分调用析构函数。
系统关闭由_Halt0
中的System
单元执行。它在执行单元终结之前执行所有退出过程。因此,您的表单在调用 class 析构函数之前被销毁。
移动编译器
请注意 DoneApplication
根本不会在 Android 上调用。
{$IFNDEF ANDROID}
AddExitProc(DoneApplication);
{$ENDIF}
这意味着主窗体的销毁是从单元终结中调用的。当每个单元完成时,它的完成部分将被执行,这会导致任何全局变量离开范围。最终,不再有对您的主窗体的引用,因此它的析构函数被执行。
如上所述,class 析构函数也从单元终结中调用。由于在 Android 上,您的 class 析构函数在主窗体被销毁之前执行,因此可以清楚地看到 uClassVar
在主窗体的最终引用被释放之前完成。
现在,这很有意义,因为 uClassVar
是初始化顺序中的最后一个单元,因此也是最终顺序中的第一个单元。如果你想确保uClassVar
稍后完成,你需要安排它尽快初始化。例如,通过像这样更改 .dpr 文件的 uses 子句:
uses
uClassVar in 'uClassVar.pas',
System.StartUpCopy,
FMX.Forms,
ufmMain in 'ufmMain.pas' {fmMain};
现在 uClassVar
是第一个初始化的单元,因此也是最后一个完成的单元。
程序:
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.
第 2 单元:
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.
Unit2 作为项目文件中的最后一个单元包含在内,但它不会首先完成,因为 Unit1 使用 Unit2 - 因此初始化顺序与 "expected" 不同。
输出如下:
Begin
Unit2.Destroy
TUnit1.Destroy
TUnit2.Destroy
我不确定为什么在这种情况下移动编译器会做一些不同的事情...
您正在使用 DisposeOf
免费组件
不要用作 .Free
或 .Destroy
示例:
Scrollbox1.Components[1].DisposeOf;
使用 Delphi XE7 Update 1 和 Delphi XE8
进行测试在 Windows OS (7 SP1 x64)、MACOSX (10.10.3) 和 Android (5.0.2) 上创建订单:
"class constructor TGlobalClass.Create;" -> "constructor TfmMain.Create;" -> "procedure TfmMain.FormCreate(Sender: TObject);"
在 Windows OS 和 MACOSX:
发布订单 "TfmMain.FormDestroy" -> "destructor TfmMain.Destroy" -> "class destructor TGlobalClass.Destroy;"
Android 发布订单:
"class destructor TGlobalClass.Destroy;" -> "TfmMain.FormDestroy" -> "destructor TfmMain.Destroy"
问题是:为什么在 Android 平台上 CLASS VAR 在主窗体之前发布?
代码示例:
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.
主机:
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.
项目文件:
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.
桌面编译器
当应用程序对象销毁其组件时,您的主窗体也被销毁。这发生在 DoneApplication
过程的 FMX.Forms
中。
procedure DoneApplication;
begin
if Screen <> nil then
Screen.ActiveForm := nil;
Application.DestroyComponents; <-- this is destroying your main form
end;
并且 DoneApplication
在关闭期间作为退出过程被调用。该退出过程是从 TApplication.Run
注册的,如下所示:
{$IFNDEF ANDROID}
AddExitProc(DoneApplication);
{$ENDIF}
Class 构造函数从定义它们的单元的初始化部分调用。因此,TGlobalClass.Create
是从 uClassVar
的初始化调用的。 Class 从同一单元的终结部分调用析构函数。
系统关闭由_Halt0
中的System
单元执行。它在执行单元终结之前执行所有退出过程。因此,您的表单在调用 class 析构函数之前被销毁。
移动编译器
请注意 DoneApplication
根本不会在 Android 上调用。
{$IFNDEF ANDROID}
AddExitProc(DoneApplication);
{$ENDIF}
这意味着主窗体的销毁是从单元终结中调用的。当每个单元完成时,它的完成部分将被执行,这会导致任何全局变量离开范围。最终,不再有对您的主窗体的引用,因此它的析构函数被执行。
如上所述,class 析构函数也从单元终结中调用。由于在 Android 上,您的 class 析构函数在主窗体被销毁之前执行,因此可以清楚地看到 uClassVar
在主窗体的最终引用被释放之前完成。
现在,这很有意义,因为 uClassVar
是初始化顺序中的最后一个单元,因此也是最终顺序中的第一个单元。如果你想确保uClassVar
稍后完成,你需要安排它尽快初始化。例如,通过像这样更改 .dpr 文件的 uses 子句:
uses
uClassVar in 'uClassVar.pas',
System.StartUpCopy,
FMX.Forms,
ufmMain in 'ufmMain.pas' {fmMain};
现在 uClassVar
是第一个初始化的单元,因此也是最后一个完成的单元。
程序:
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.
第 2 单元:
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.
Unit2 作为项目文件中的最后一个单元包含在内,但它不会首先完成,因为 Unit1 使用 Unit2 - 因此初始化顺序与 "expected" 不同。
输出如下:
Begin
Unit2.Destroy
TUnit1.Destroy
TUnit2.Destroy
我不确定为什么在这种情况下移动编译器会做一些不同的事情...
您正在使用 DisposeOf
免费组件
不要用作 .Free
或 .Destroy
示例:
Scrollbox1.Components[1].DisposeOf;