Oft wird Delphi-Neulingen geraten, bei der Arbeit mit Objekten die Referenzen nach der Freigabe eines Objekts (zB mit
FreeAndNil) wieder auf
nil zu setzen, damit ein Vergleich der Form
if Assigned(AnObject) then
eingesetzt werden kann, um zu überprüfen, ob es sich um eine gültige Referenz handelt.
Der Compiler von Delphi initialisiert Objektreferenzen innerhalb von Methoden und Prozeduren/Funktionen nicht vor, so dass ein Test der Art
Delphi-Quellcode:
procedure MyProc;
var
myObject: TObject;
begin
if Assigned(myObject) then
zwar überprüft, ob die Referenz (hier:
myObject) einen Wert hält, der von
nil verschieden ist, kann aber im Fall einer "durch Zufall" von
nil verschiedenen "Vorbelegung" nicht entscheiden, ob der Wert nun tatsächlich auf ein Objekt verweist.
Innerhalb des
DEC und der JCL konnte ich nun zwei verschiedene Implementierungen einer Funktion
IsObject (bei der JCL auch eine
IsClass für Klassenreferenzen) finden, die zwei unterschiedliche Ansätze verfolgen.
Die Lösung von Hagen prüft, sofern ich sie nicht missdeute, ob vor dem Speicher, in dem das vermeintliche Objekt liegt, einen MagicCode enthält (genauer ein Bit), der vom Borland-Speichermanager dort hinterlegt worden sein sollte.
Die Lösung innerhalb der JCL geht davon aus, dass ein Objekt zu einer Klasse gehört und überprüft innerhalb der
VMT der Klasse, ob die dort erwartete Referenz auf die Klasse selbst vorhanden ist.
Während Hagens Ansatz schneller arbeiten sollte kann bei zufälligen Werten (durch eine zufällig vorbelegte lokale Referenz) durch seine Maskierung der Form
if MagicCode and $2 = $2 then
nur zu 50% gesagt werden, ob es sich um ein Objekt handelt. Aus informationstheoretischer Sicht ist dies keine Information, also nutzlos für diesen Fall, obgleich die Routine für andere Szenarien korrekt arbeiten sollte. Darüber hinaus könnte dieser Ansatz nicht mehr funktionieren, wenn man die Speichervergabe von Objekten über eine eigene Implementierung (NodeManager) lösen würde, die ohne den MagicCode auskommt.
Der Ansatz der JCL prüft zwar ein wesentlich unwahrscheinlicheres Kriterium, hat aber gegenüber der Lösung aus dem
DEC den Nachteil, das er mit diversen Indirektionen arbeitet, also im Speicher liest, obgleich es bei der Interpretation von nicht-initialisierten Referenzen sein kann, dass keine Leserechte zu den entsprechenden Speicherbereichen existieren. Hagens Lösung hingegen "schluckt" solche Fehler (durch einen try-except-Block) und gibt für diesen Fall den erwarteten Wert
False zurück.
In der Vergangenheit habe ich eine zur JCL ähnliche Lösung entwickelt, die mithilfe der funktion
IsBadReadPtr überprüft, ob die Bereiche gelesen werden können und außerdem beim Sonderfall
nil abkürzt, so dass sie als Alternative für
Assigned eingesetzt werden könnte.
Leider ist diese Implementierung etwas langsamer als der Ansatz der JCL und bei weitem inperformanter als die Lösung aus dem
DEC.
Darüber hinaus vermag auch dieser Ansatz (genauso wie die beiden anderen) nicht das volgende Szenarium korrekt zu erfassen
Delphi-Quellcode:
var
myObject: TObject;
begin
myObject:= TObject.Create;
myObject.Free;
if IsObject(myObject) then
weil der Speicher hinter dem durch den Destructor freigegeben Objekt nicht gelöscht wird und somit weiterhin auf eine gültige Klasse verweist. Auch der MagicCode des Borland-Speichermanagers zeigt weiterhin das geforderte Kriterium, so dass ich bisher keine geeignete Lösung des Problems zur
identifizierung einer gültigen Objektreferenz ausmachen konnte.
Hat jemand eine Lösung?