Validität feststellen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Validität feststellen

Beitrag von diogenes »

Ist es eigentlich möglich (und wenn ja: wie?) festzustellen, ob eine Objektvariable auf ein gültiges Objekt zeigt, bevor man es freigibt?
Ceterum censeo computatores per Pascal docendos esse.

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Validität feststellen

Beitrag von Scotty »


mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Validität feststellen

Beitrag von mschnell »

diogenes hat geschrieben:Ist es eigentlich möglich (und wenn ja: wie?) festzustellen, ob eine Objektvariable auf ein gültiges Objekt zeigt, bevor man es freigibt?
Nö. Nach dem Freigeben kann der Pointer auf irgendwas zeigen. Daran kann man dann auch nichts mehr erkennen.

Zu diesem Zweck gibt es "FreeAndNIL", das den Pointer auf das freigegeben Objekt nullt. Aber nur diesen Pointer, alle anderen, wohin man die Objekt-referenz irgendwann einmal kopiert hat bleiben ungleich NIL und ungültig.

Nowas geht sinnvoll nur in C# und ähnlichen Sprachen mit Garbage-Control.

-Michael

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: Validität feststellen

Beitrag von diogenes »

FreeAndNIL ist mir bekannt. Danke jedenfalls für diese Information, die zwar enttäuschend ist, aber immerhin lernt man auch dabei was. Einen SIGSEGV könnt' man aber mit try ... except/finally ... end abfangen, oder?
Ceterum censeo computatores per Pascal docendos esse.

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2809
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Validität feststellen

Beitrag von m.fuchs »

diogenes hat geschrieben:Ist es eigentlich möglich (und wenn ja: wie?) festzustellen, ob eine Objektvariable auf ein gültiges Objekt zeigt, bevor man es freigibt?
Warum willst du das prüfen? Ein Aufruf von .Free bei einem ungültigen Objekt schadet doch nicht.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Validität feststellen

Beitrag von theo »

m.fuchs hat geschrieben: Warum willst du das prüfen? Ein Aufruf von .Free bei einem ungültigen Objekt schadet doch nicht.

Ach ja? Aber eine Zugriffsverletzung gibt's schon, oder?

Code: Alles auswählen

var Sl:TStringList;
begin
 Sl:=TStringList.Create;
 Sl.Free;
 Sl.Free;
end; 


Wenn's nil ist geht das:

Code: Alles auswählen

var Sl:TStringList;
begin
 Sl:=TStringList.Create;
 FreeAndNil(Sl);
 Sl.Free;
end;

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2809
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Validität feststellen

Beitrag von m.fuchs »

Ah, Irrtum meinerseits. Mir war so, als ob .Free eine besondere Magie hätte, die solche Fälle abfängt. Aber das gilt natürlich nur, wenn die Referenz korrekt auf nil gesetzt wird.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: Validität feststellen

Beitrag von diogenes »

Eben. Deswegen meine Fragen.
Ceterum censeo computatores per Pascal docendos esse.

martin_frb
Beiträge: 586
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: Validität feststellen

Beitrag von martin_frb »

Nein einen Test gibt es nicht.

Man könnte zwar bestimmte Kriterien zu prüfen suchen (Speicher allocatiert? fpc interne Strukturen), aber sicher ist es trotzdem nicht.
1) Ein anderes Objekt koennte mittlerweile an diesem platz seien
2) Das alte Objekt ist zwar freigegeben, aber der Speicher noch nicht (oder nur teilweise) überschrieben). Dann sieht es zunaechst so aus als ob es noch da waere.

Wenn du mehrere variablen hast die auf das selbe Objekt zeigen, dann kannst du:
1) TComponent.FreeNotification
2) Andere Objecte, definiere selbst ein event (event liste) das bei Freigabe aufgerufen wird

1&2) Und dann kann fuer jede variable ein event listener eingetragen werden, und das Objekt zerstoeren.

3) Implementiere eine RefCounted Loesung (TRefCountedObject ist in lazutils IIRC)

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Validität feststellen

Beitrag von Socke »

Bei Objekten ist diese Regel allgemein üblich:
Ist die Adresse gleich nil, ist kein Objekt vorhanden. Ist die Adresse eine andere, geht man davon aus, dass ein Objekt existiert.

Im Programmfluss sieht das so aus:

Code: Alles auswählen

procedure myproc;
var
  o: TObject;
begin
  // 1. o hat einen unbekannten Wert, da die Variable in Pascal nicht initialisiert wird.
  // 2. Der Variablen einen _bekannten_ Wert zuweisen.
  o := nil;  // nil = kein Objekt. Jetzt kann man auf diesen Wert prüfen.
  o := TObject.Create; // ein Objekt erzeugen. Falls dies nicht erfolgreich ist, wird eine Exception ausgelöst und o muss wie unter 1. behandelt werden.
  // 3. Objekt freigeben
  o.Destroy; // hier _muss_ das Objekt existieren
  // 4. o auf einen bekannten Wert (nil) setzen.
  o := nil; // jetzt kann wieder auf nil überprüft werden.
 
  // eine Überprüfung kann so aussehen:
  if o <> nil then
  // oder alternativ (beide Varianten sind äquivalent)
  if Assigned(o) then
    o.myfunc;
 
  // Free als Alternative zu Destroy:
  o := TObject.Create;
  o.Free; // Objekt freigeben
  o := nil;
  // Die Funktion Free überprüft, ob das aktuelle Objekt (Self) ungleich nil ist. Nur dann wird auch der Destruktor aufgerufen.
  o.Free;  // Objekt wird nicht freigeben, da o = nil ist. Hier tritt keine Access Violation auf
 
  // Die Funktion FreeAndNil() nichts anderes als das Folgende (nur als fertige Funktion):
  o.Free;
  o := nil;
end;
Damit ist das martin_frb angesprochene Problem bei verknüpften Objekten noch nicht ganz gelöst. Wird ein Objekt freigegeben, müssen -- wie er ausgeführt hat -- auch die in anderen Objekten abgelegten Referenzen gelöscht (oder auf nil gesetzt) werden (wenn nil entsprechend behandelt wird).
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Validität feststellen

Beitrag von mse »

Socke hat geschrieben: Damit ist das martin_frb angesprochene Problem bei verknüpften Objekten noch nicht ganz gelöst. Wird ein Objekt freigegeben, müssen -- wie er ausgeführt hat -- auch die in anderen Objekten abgelegten Referenzen gelöscht (oder auf nil gesetzt) werden (wenn nil entsprechend behandelt wird).
Falls es euch interessiert, MSEgui hat dafür einen Mechanismus in tmsecomponent mit iobjectlink, implementiert durch tobjectlinker. Eine tmsecomponent Variable gesetzt mit tmsecomponent.setlinkedvar() wird automatisch nil gesetzt beim Freigeben des Objektes. Der iobjectlink Nachkomme ievent wird zudem zur Kommunikation unter den verbundenen Objekten verwendet, siehe mseclasses.pas.
http://gitorious.org/mseide-msegui/msei ... lasses.pas

Code: Alles auswählen

 
 objecteventty = (oe_destroyed,oe_connect,oe_disconnect,
                  oe_changed,oe_designchanged,
                  oe_activate,oe_deactivate,oe_fired,oe_dataready,
                  oe_bindfields,oe_releasefields);
 objectlinkeventty = procedure(const sender: tobject;
                    const event: objecteventty) of object;
 iobjectlink = interface(inullinterface)
  procedure link(const source,dest: iobjectlink; valuepo: pointer = nil;
                        ainterfacetype: pointer = nil; once: boolean = false);
  procedure unlink(const source,dest: iobjectlink; valuepo: pointer = nil);
               //source = 1 -> dest destroyed
  procedure objevent(const sender: iobjectlink; const event: objecteventty);
  function getinstance: tobject;
 end;
 
 [...]
 
 tobjectevent = class;
 
 ievent = interface(iobjectlink)
  procedure receiveevent(const event: tobjectevent);
 end;
 
 objeventstatety = (oes_islinked,oes_modaldeferred);
 objeventstatesty = set of objeventstatety;
 
 tobjectevent = class(tmseevent,iobjectlink)
  private
   finterface: pointer; //ievent;
   procedure link(const source,dest: iobjectlink; valuepo: pointer = nil;
                 ainterfacetype: pointer = nil; once: boolean = false);
   procedure unlink(const source,dest: iobjectlink; valuepo: pointer = nil);
   procedure objevent(const sender: iobjectlink; const event: objecteventty);
   function getinstance: tobject;
  protected
   fstate: objeventstatesty;
   fmodallevel: integer;
  public
   constructor create(akind: eventkindty; const dest: ievent;
                                           const modaldefer: boolean = false);
   destructor destroy; override;
   procedure deliver;
   property modallevel: integer read fmodallevel;
 end;
 
Martin

Antworten