2011-10-04 5 views
10

Come creare e liberare in modo sicuro più oggetti?Come creare e liberare in modo sicuro più oggetti in Delphi

In sostanza, questo genere di cose:

newOrderSource := TWebNewOrderSource.Create(); 
    twData := TTWData.Create(); 
    webData := TWebData.Create(); 

    try 
    //do stuff 
    finally 
    newOrderSource.Free(); 
    twData.Free(); 
    webData.Free(); 
    end; 

In questo caso, il secondo e il terzo creare comandi non sono sicuri, mentre lavorano con un database. Devo solo mettere tutti i Creates nel blocco try e controllare se sono assegnati prima che io li chiami gratis?

risposta

15

Si può fare questo con un blocco try se si assegna nil alle variabili prima come,

newOrderSource := nil; 
twData := nil; 
webData := nil; 
try 
    newOrderSource := TWebNewOrderSource.Create();  
    twData := TTWData.Create();  
    webData := TWebData.Create();  

    //do stuff  
finally  
    webData.Free();  
    twData.Free();  
    newOrderSource.Free();  
end;  

Questo funziona perché Free() controlli Self per nil.

+0

stavo postando una risposta molto simile, una piccola modifica sarebbe quella di mettere 'newOrderSource: = TWebNewOrderSource.Create() ; 'prima del tentativo, e butta la' newOrderSource: = nil; '. –

+2

Anche questo funziona e rimuove un compito non necessario, ma ho preferito la semplicità e la coerenza in questo caso perché è altamente improbabile che il salvataggio del compito farebbe molta differenza se "fai roba" chiama in un database. – chuckj

+6

Io lo uso sempre, ma per quanto riguarda la sicurezza, si dovrebbe notare che, rispetto al codice originale di OP, questo protegge da eccezioni sollevate in qualsiasi costruttore ma ** non ** da eccezioni generate durante la liberazione degli oggetti. Se sei paranoico a riguardo, la tua unica opzione è quella di avere tanti try/finally in quanto ci sono oggetti (o averli implementare interfacce e lasciarli andare fuori ambito). –

10

Come sono sicuro che tutti sanno, il modo standard di gestire un oggetto è come questo:

A := TMyObject.Create; 
try 
    A.DoSomething; 
finally 
    A.Free; 
end; 

Se c'è un'eccezione nella TMyObject.Create allora il distruttore sarà chiamato e quindi l'eccezione sollevata. In tal caso, non sarà assegnato a A.

Quando si dispone di più oggetti è possibile ripetere il modello:

A := TMyObject.Create; 
try 
    B := TMyObject.Create; 
    try 
    A.DoSomething; 
    B.DoSomething; 
    finally 
    B.Free; 
    end; 
finally 
    A.Free; 
end; 

Questo diventa molto rapidamente un pasticcio e quindi la domanda.

Un trucco standard è quello di sfruttare il fatto che Free può essere tranquillamente chiamato su un riferimento all'oggetto nil.

A := nil; 
B := nil; 
try 
    A := TMyObject.Create; 
    B := TMyObject.Create; 
    A.DoSomething; 
    B.DoSomething; 
finally 
    B.Free; 
    A.Free; 
end; 

questo ha la debolezza minore che non è resistente alle eccezioni essendo sollevate B.Free ma non è irragionevole considerare questo come una condizione di errore che può essere ignorato. I distruttori non dovrebbero sollevare eccezioni. Se lo fanno allora il tuo sistema è probabilmente rotto irrimediabilmente.

Questo schema sopra può diventare un po 'disordinato man mano che vengono aggiunti più oggetti, quindi uso personalmente i seguenti metodi di supporto.

procedure InitialiseNil(var Obj1); overload; 
procedure InitialiseNil(var Obj1, Obj2); overload; 
procedure InitialiseNil(var Obj1, Obj2, Obj3); overload; 

procedure FreeAndNil(var Obj1); overload; 
procedure FreeAndNil(var Obj1, Obj2); overload; 
procedure FreeAndNil(var Obj1, Obj2, Obj3); overload; 

Infatti il ​​mio codice ha versioni con ancora più parametri. Per facilità di manutenzione questo codice viene generato automaticamente da un breve script Python.

Questi metodi sono implementati in modo ovvio, ad es.

procedure FreeAndNil(var Obj1, Obj2); 
var 
    Temp1, Temp2: TObject; 
begin 
    Temp1 := TObject(Obj1); 
    Temp2 := TObject(Obj2); 
    Pointer(Obj1) := nil; 
    Pointer(Obj2) := nil; 
    Temp1.Free; 
    Temp2.Free; 
end; 

Questo ci permette di riscrivere l'esempio precedente come questo:

InitialiseNil(A, B); 
try 
    A := TMyObject.Create; 
    B := TMyObject.Create; 
    A.DoSomething; 
    B.DoSomething; 
finally 
    FreeAndNil(B, A); 
end; 
+0

Non potresti avere i tuoi metodi di supporto per ricevere una matrice di valori in modo da poter passare un numero arbitrario di oggetti? –

+0

@Jerry Funzionerebbe anche, ma a questo punto ho preferito non utilizzare la sintassi di array aperta. Il mio ragionamento è che queste funzioni sono scritte una volta e mai modificate. Quindi mi interessa meno della duplicazione rispetto a quella del codice vivente che viene modificato. La motivazione è che il codice chiamante è più pulito e questo è il codice che viene modificato. –

Problemi correlati