2010-12-08 15 views
4

Ich frage mich, wenn eine Instanz einer TInterfacedObject abgeleiteten Klasse zerstört wird und wer den Destruktor aufruft. Ich habe eine ScopedLock-Klasse geschrieben, die automatisch die Methode Release eines Synchronisierungsobjekts aufrufen sollte, wenn die Instanz den Gültigkeitsbereich verlässt. Es ist ein RAII-Konzept, das aus C++ bekannt ist, aber ich weiß nicht, ob es garantiert ist, dass der Destruktor aufgerufen wird, wenn die Sperrinstanz den Gültigkeitsbereich verlässt.Wann wird TInterfacedObject.Destroy aufgerufen (eine ScopedLock-Klasse)

ILock = interface 
end; 

ScopedLock<T: TSynchroObject> = class(TInterfacedObject, ILock) 
strict private 
    sync_ : T; 
public 
    constructor Create(synchro : T); reintroduce; 
    destructor Destroy;override; 
end; 

implementation 
{ ScopedLock<T> } 

constructor ScopedLock<T>.Create(synchro: T); 
begin 
    inherited Create;; 
    sync_ := synchro; 
    sync_.Acquire; 
end; 

destructor ScopedLock<T>.Destroy; 
begin 
    sync_.Release; 
    inherited; 
end; 

{ Example } 
function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    // ... 
end; // mySync released ? 

Es funktioniert gut in einem einfachen Testfall, aber ist es sicher?

Antwort

5

Ja, das ist sicher. Ihr Code

function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    // ... 
end; 

wie der folgenden Pseudo-Code kompiliert wird

function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    lock._AddRef; // ref count = 1 
    try 
// .. 
    finally 
    lock._Release; // ref count = 0, free lock object 
    end; 

Sie können sehen, dass, wenn Schloss var den Gültigkeitsbereiches verläßt seine ref Zahl verringert wird, wurde Null und Sperrobjekt wird automatisch zerstört.

6

Das einzige Problem ist, wenn die Funktion inline ist: Die lokale Variable, die die ILock-Referenz enthält, wird in den Bereich der Funktion hochgestuft, die die inline-Funktion aufruft. Das kann dazu führen, dass das Schloss länger als gewünscht funktioniert.

Auf der anderen Seite ist es nicht notwendig, eine Variable zu deklarieren, die die Schnittstellenreferenz enthält, wenn Sie eine Funktion schreiben (z. B. eine Klassenfunktion namens Create), die eine Schnittstellenreferenz (im Gegensatz zu einer Objektreferenz) zurückgibt. Der Compiler erstellt ein verstecktes Local, um den Rückgabewert zu erhalten (da alle verwalteten Typen wie Interfaces und Strings tatsächlich zurückgegeben werden, indem eine Ergebnisvariable übergeben wird). Dieses versteckte Lokal verhält sich genau wie das explizite Local.

Ich schrieb mehr darüber hier: http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

+1

Guter Artikel .. Können Sie mir sagen, wie der Delphi-Compiler entscheidet, eine Funktion zu inline oder nicht? Gibt es Methoden, Inlining zu verhindern/durchzusetzen? – hansmaad

+0

@hansmaad: Sofern Sie {$ INLINE AUTO} nicht aktiviert haben, wird eine Funktion, die nicht mit der Inline-Direktive markiert ist, nicht automatisch eingebunden. Aber $ INLINE AUTO verwendet keine sehr intelligente Heuristik - es inline jede Funktion unterhalb einer bestimmten Größe und kann leicht zu einer Größenexplosion führen. Dies bedeutet, dass $ INLINE AUTO wahrscheinlich nicht in Kraft ist. Sie brauchen sich also nur darum zu kümmern, wenn Sie die Funktion zum Inlining mit der Inline-Direktive am Ende des Prozedurheaders selbst markieren. –

0

Ich sehe nicht, dass der Ansatz, den Sie eintreten, während richtig, eigentlich besser als gute alte try/finally. Sie haben eine klare und eindeutige Lösung gewählt und sie durch eine undurchsichtige und undurchsichtige ersetzt.

Echte Arbeit Delphi-Code ist voll von Try/Endlich und so sollte es ein natürliches Idiom sein. Ich kann den Nachteil des Schreibens nicht sehen:

+0

Weil es weniger Code ist? Oder weil der Geltungsbereich an dem Punkt, an dem er erstellt wird, explizit sichtbar wird. Es gibt wahrscheinlich auch andere Gründe. Versuchen Sie,/endlich ist nicht schlecht, aber andere Techniken können auch gut sein, Sie wissen :) –

+0

@David M vorausgesetzt, dass Sie zum Lesen und Parsen verwendet werden müssen Versuchen/Schließlich sowieso dann können Sie es auch verwenden, wo immer es den Job macht. Mehrere Möglichkeiten zum Schutz einer Ressource (Leaking oder Serialisierung), die sowieso nur für mich relevant sind, führen zu zusätzlichen Komplikationen beim Lesen und Überprüfen von Code. Ich schätze, ich bin von der Python-Schule "One way to do sachen" im Gegensatz zur Perl-Schule "vieler Wege" !! –

+4

@Daivd H: Für mich ist der "eine Weg, Dinge zu tun", RAII-Techniken zu verwenden, wann immer es möglich ist.Ich kann damit nichts falsch machen. Wann immer ich eine Serialisierung erwerbe, bin ich sicher, dass ich die Veröffentlichung am richtigen Ort bekomme, ohne zusätzliche Codezeilen zu schreiben, die ich früher oder später vergessen werde. – hansmaad

Verwandte Themen