2015-06-26 8 views
5

Testato con Delphi XE7 Update 1 e Delphi XE8

Creare ordine sul sistema operativo Windows (7 SP1 x64), MACOSX (10.10.3) e Android (5.0.2):Delphi XE8. FMX. Perché è diverso l'ordine di rilascio di CLASS VAR sulla piattaforma Android?

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

ordine di uscita su sistema operativo Windows e MacOSX:

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

ordine di uscita su Android:

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

La domanda è: perché sulla piattaforma Android CLASS VAR rilasciare prima del modulo principale?

Esempio di codice:

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à principale:

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. 

file di progetto:

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

Non possiamo vedere dove 'fmMain' è dichiarato, creato, distrutto ecc. Dov'è il resto del codice. –

+0

@DavidHeffernan - File di progetto aggiunto. Questo è tutto il codice di questo progetto di test. – Zam

+0

Grazie per l'aggiornamento. Ho cercato di spiegare cosa sta succedendo in una risposta. –

risposta

5

compilatori per il desktop

È La forma principale viene distrutta quando l'oggetto dell'applicazione distrugge i suoi componenti. Ciò accade in FMX.Forms nella procedura DoneApplication.

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

E DoneApplication è chiamato durante l'arresto come proc uscita. Questo proc uscita è iscritto da TApplication.Run come questo:

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

costruttori della classe sono chiamati dalla sezione di inizializzazione del gruppo, che li definisce. Quindi, TGlobalClass.Create viene chiamato dall'inizializzazione di uClassVar. I distruttori di classe sono chiamati dalla sezione di finalizzazione di quella stessa unità.

L'arresto del sistema viene eseguito dall'unità System in _Halt0. Esegue tutti i proc di uscita prima di eseguire la finalizzazione dell'unità. Quindi la tua forma viene distrutta prima che vengano chiamati i distruttori di classe.

compilatori mobili

Nota che DoneApplication non è semplicemente chiamato su Android.

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

Ciò significa che la distruzione della maschera principale viene invocata dalla finalizzazione dell'unità. Man mano che ogni unità viene finalizzata, vengono eseguite le sezioni di finalizzazione che portano a qualsiasi variabile globale che lasci spazio. Alla fine, non ci sono più riferimenti al tuo modulo principale e quindi viene eseguito il suo distruttore.

Come discusso sopra, i distruttori di classe vengono anche chiamati dalla finalizzazione dell'unità. Poiché su Android, il distruttore di classe viene eseguito prima che il modulo principale venga distrutto, è evidente che lo uClassVar viene finalizzato prima che venga rilasciato il riferimento finale del modulo principale.

Ora, questo ha perfettamente senso perché uClassVar è l'unità finale nell'ordine di inizializzazione, e quindi la prima unità nell'ordine di finalizzazione. Se si desidera assicurarsi che lo standard uClassVar sia finalizzato in un secondo momento, è necessario predisporre l'inizializzazione prima possibile. Per esempio, modificando la clausola uses del file .dpr in questo modo:

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

Ora uClassVar è la prima unità inizializzato, e, quindi, l'ultima unità finalizzato.

+0

Ma perché funziona su iOS? È anche una piattaforma mobile. – Zam

+0

Sì. Perché su iOS 'DoneApplication' è registrato come un exit proc. –

+0

"I costruttori di classe sono richiamati dalla sezione di inizializzazione" l'istruzione più corretta sarebbe I caratteri di classe vengono chiamati _before_ la sezione di inizializzazione viene eseguita e gli agenti di classe vengono chiamati _after_ la sezione di finalizzazione viene eseguita. –

0

Il programma:

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. 

Ci Unit2 è incluso come l'ultima unità nel file di progetto, ma non sarà finalizzato prima come Unità1 utilizza l'Unità2 - quindi l'ordine di inizializzazione è diverso dal "previsto".

L'uscita è la seguente:

Begin 
Unit2.Destroy 
TUnit1.Destroy 
TUnit2.Destroy 

io non sono sicuro perché in tal caso il compilatore cellulare potrebbe fare qualcosa di diverso ...

+0

@David Heffernan: O è diverso sulle piattaforme mobili? –

+0

Hai letto abbastanza bene la mia risposta? Il punto è che su ** tutte ** le piattaforme diverse da Android, la forma principale viene distrutta ** prima che inizi la finalizzazione dell'unità. Hai osservato il punto che DoneApplication non viene chiamata dalla finalizzazione, ma viene chiamata da un proc di uscita. E hai notato che i proc di uscita vengono chiamati prima di qualsiasi finalizzazione di unità. E hai notato l'esplicito {$ IFNDEF ANDROID} che significa che DoneApplication non è registrato come un exit proc? –

+0

Errore mio, @DavidHeffernan. Ho appena controllato il problema in dettaglio. –

-1

Si utilizza DisposeOf per i componenti gratuiti

Don utilizzare come .Free o .Destroy

Esempio:

Scrollbox1.Components[1].DisposeOf; 
Problemi correlati