2015-06-26 19 views
5

Getestet mit Delphi XE7 Update 1 und Delphi XE8

um unter Windows OS erstellen (7 SP1 x64), Mac OS X (10.10.3) und Android (5.0.2):Delphi XE8. FMX. Warum unterscheidet sich die Reihenfolge von CLASS VAR auf der Android-Plattform?

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

Abruf auf Windows-Betriebssystem und MACOSX:

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

Abruf auf Android:

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

Frage ist: Warum auf Android-Plattform CLASS VAR Freigabe vor Hauptformular?

Probe 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. 

Hauptgerät:

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. 

Projektdatei:

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. 
+0

Wir können nicht sehen, wo 'fmMain' deklariert, erstellt, zerstört usw. Wo ist der Rest des Codes. –

+0

@DavidHeffernan - Projektdatei hinzugefügt. Das ist alles Code aus diesem Testprojekt. – Zam

+0

Vielen Dank für das Update. Ich habe versucht zu erklären, was in einer Antwort passiert. –

Antwort

5

Desktop-Compiler

Sie Das Hauptformular wird zerstört, wenn das Anwendungsobjekt seine Komponenten zerstört. Das passiert in FMX.Forms in der DoneApplication Prozedur.

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

Und DoneApplication wird während des Abschaltens als Exit-proc genannt. Das Exit proc von TApplication.Run wie folgt registriert:

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

Klassenkonstruktoren werden aus dem Initialisierungsabschnitt der Einheit genannt, die sie definiert. So wird TGlobalClass.Create von der Initialisierung uClassVar aufgerufen. Klassendestruktoren werden vom Finalisierungsabschnitt derselben Einheit aufgerufen.

Das Herunterfahren des Systems wird durch die System Einheit in _Halt0 durchgeführt. Es führt alle Exit-Prozesse aus, bevor die Finalisierung der Einheit ausgeführt wird. Daher wird Ihre Form zerstört, bevor die Klassenzerstörer aufgerufen werden.

Mobil Compiler

Beachten Sie, dass DoneApplication einfach nicht auf Android genannt wird.

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

Dies bedeutet, dass die Zerstörung des Hauptformulars durch die Finalisierung der Einheit aufgerufen wird. Wenn jede Einheit abgeschlossen ist, werden ihre Finalisierungsabschnitte ausgeführt, was dazu führt, dass irgendwelche globalen Variablen den Geltungsbereich verlassen. Schließlich gibt es keine Referenzen mehr auf Ihr Hauptformular und der Destruktor wird ausgeführt.

Wie oben besprochen, werden die Klassendestruktoren auch von der Finalisierung der Einheit aus aufgerufen. Da Ihr Klassendestruktor bei Android ausgeführt wird, bevor das Hauptformular gelöscht wird, ist klar, dass uClassVar abgeschlossen ist, bevor die endgültige Referenz des Hauptformulars freigegeben wird.

Nun, das macht Sinn, denn uClassVar ist die letzte Einheit in der Reihenfolge der Initialisierung und damit die allererste Einheit in der Finalisierungsreihenfolge. Wenn Sie sicherstellen möchten, dass uClassVar später abgeschlossen wird, müssen Sie veranlassen, dass es früher initialisiert wird. Zum Beispiel durch eine Änderung der uses-Klausel der .dpr Datei wie folgt:

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

Jetzt uClassVar ist die erste Einheit initialisiert und damit die letzte Einheit abgeschlossen.

+0

Aber warum funktioniert es in iOS? Es ist auch eine mobile Plattform. – Zam

+0

Ja. Weil auf iOS 'DoneApplication' als ein Exit-Prozess registriert ist. –

+0

"Klassenkonstruktoren werden aus dem Initialisierungsabschnitt aufgerufen" Korrektere Anweisung wäre Klasse ctors heißen _before_ initialization Abschnitt ausgeführt wird, und Klasse dtors werden aufgerufen _after_ finalization Abschnitt ausgeführt wird. –

0

Das Programm:

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. 

Es Unit2 als die letzte Einheit in der Projektdatei enthalten ist, aber es wird nicht sein Zuerst abgeschlossen, da Unit1 die Unit2 verwendet - also unterscheidet sich die Initialisierungsreihenfolge von der erwarteten.

Der Ausgang folgt:

Begin 
Unit2.Destroy 
TUnit1.Destroy 
TUnit2.Destroy 

Ich bin nicht sicher, warum in einem solchen Fall die mobile Compiler etwas anderes tun würde ...

+0

@David Heffernan: Oder ist das anders auf den mobilen Plattformen? –

+0

Haben Sie meine Antwort genau genug gelesen? Der Punkt ist, dass auf ** allen ** Plattformen außer Android das Hauptformular zerstört wird ** bevor ** die Finalisierung der Einheit beginnt. Haben Sie beobachtet, dass DoneApplication nicht von der Finalisierung aufgerufen wird, sondern von einem Exit-Prozess aufgerufen wird. Und hast du bemerkt, dass die Exit-Procs vor allen Finalisierungen der Einheiten aufgerufen werden. Und haben Sie das explizite {$ IFNDEF ANDROID} notiert, was bedeutet, dass DoneApplication nicht als Exit-Prozess registriert ist? –

+0

Meine Schuld, @DavidHeffernan. Ich habe das Thema genau untersucht. –

-1

Sie verwenden DisposeOf For Free Components

Don nicht verwenden als .Free oder .Destroy

Beispiel:

Scrollbox1.Components[1].DisposeOf;