Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Bin ich schon zerstört? (https://www.delphipraxis.net/56601-bin-ich-schon-zerstoert.html)

Sidorion 8. Nov 2005 15:51


Bin ich schon zerstört?
 
Kann man irgendwie herausfinden, ob die aktuelle Instanz bereits zerstört ist?
Im besonderen Fall rufe ich bei einer eventuell schon zerstörten Instanz eine Methode auf, wobei in dieser Methode auf einen Member der Instanz zugegriffen wird.
In der momentanen Implementierung ist dieser Methodenaufruf mit einem try .. except- Block gesichert, mich nerven aber die trotzdem auftretenden Delphi-exceptions mit Anhalten des Debuggers (nein ich will nicht 'bei Delphi-exceptions stoppen' ausschalten).
Ziel der Übung wäre, in der gerufenen Methode der evtl. schon zerstörten Instanz, eben diesen 'zerstört Status' abzuprüfen, ohne mit try except zu arbeiten und nur auf den Member zuzugreifen, falls die Instanz noch lebt.

-NIP- 8. Nov 2005 16:01

Re: Bin ich schon zerstört?
 
Wenn mich nicht alles täuscht kann die Komponente doch garkeine Methode mehr ausführen, falls sie schon zerstört wurde?

Ich kenne nur Methoden, die es vor dem Createn ausführen kann.

Bernhard Geyer 8. Nov 2005 16:01

Re: Bin ich schon zerstört?
 
Wie wird die Instanz zerstört?
Kann diese Instanz evtl. die entsprechende Referenz auf nil setzen?

DGL-luke 8. Nov 2005 16:02

Re: Bin ich schon zerstört?
 
Delphi-Quellcode:
if assigned(myobj) then
 dosomething;
wenn der zeiger anständig nil gesetzt wurde nach dem zerstören.

Sidorion 8. Nov 2005 16:07

Re: Bin ich schon zerstört?
 
@-NIP-: Methoden einer Klasse können IMMER ausgeführt werden, allserdings kann man auf die Member nur zugreifen, wenn ne instanz da ist.

@Bernhard: Die Instanz wird ganz normal zerstürt, habe aber zwei Zeiger drauf und die wissen voneinander nix.

@DGL-Luke: darum ist Assigned(MyObj) in dem Falle nach wie vor true, das ist ja das Problem. Wenn nicht, würd ich die Methode ja nicht mehr rufen.

phXql 8. Nov 2005 16:48

Re: Bin ich schon zerstört?
 
methoden einer klasse können nur aufgerufen werden wenn sie static sind.. die "anderen" methoden kann man nur aufrufen, wenn man auch eine instanz hat...

Khabarakh 8. Nov 2005 16:51

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von phXql
methoden einer klasse können nur aufgerufen werden wenn sie static sind.. die "anderen" methoden kann man nur aufrufen, wenn man auch eine instanz hat...

Wenn das so wäre, wäre Free ziemlich sinnlos :wink: .

phXql 8. Nov 2005 16:53

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von Khabarakh
Zitat:

Zitat von phXql
methoden einer klasse können nur aufgerufen werden wenn sie static sind.. die "anderen" methoden kann man nur aufrufen, wenn man auch eine instanz hat...

Wenn das so wäre, wäre Free ziemlich sinnlos :wink: .

Bei Free hat man ja auch schon eine Instanz, die man freigeben will ;)

SirThornberry 8. Nov 2005 17:08

Re: Bin ich schon zerstört?
 
man kann aber auch einfach auf eine "nil" instanz eine methode anwenden. Solange in der Methode (wie schon geschrieben wurde) nicht auf membervariablen zugegriffen wird kommt es da auch nicht zum Fehler.
Delphi-Quellcode:
TKlasse(nil).Methode;
In diesem fall ist dann nur "Self" innerhalb der Methode nil

Khabarakh 8. Nov 2005 17:13

Re: Bin ich schon zerstört?
 
Genau. Es wäre ja wirklich sinnlos, wenn in Free Self auf nil geprüft wird, aber schon beim Methodenaufruf wegen Self = nil eine Exception ausgelöst würde.

phXql 8. Nov 2005 18:24

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von SirThornberry
man kann aber auch einfach auf eine "nil" instanz eine methode anwenden. Solange in der Methode (wie schon geschrieben wurde) nicht auf membervariablen zugegriffen wird kommt es da auch nicht zum Fehler.
Delphi-Quellcode:
TKlasse(nil).Methode;
In diesem fall ist dann nur "Self" innerhalb der Methode nil

ja, aber damit gaukelst du doch dem compiler vor, eine instanz zu haben.

Du kannst ja nicht TKlasse.Free aufrufen, wenn Free() keine static-methode ist...

alzaimar 8. Nov 2005 18:37

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von Khabarakh
Genau. Es wäre ja wirklich sinnlos, wenn in Free Self auf nil geprüft wird, aber schon beim Methodenaufruf wegen Self = nil eine Exception ausgelöst würde.

Free ist aber genauso definiert:
Delphi-Quellcode:
Procedure TObject.Free;
Begin
  If Self<>Nil Then Destroy;
End;
Das dient genau dazu, Nil-Referenzen auf Klassen 'aus Versehen' nochmals freizugeben, ohne das einem das Programm um die Ohren ballert.
Du kannst ohne Probleme TFooObject(4711).SomeMethod ausführen. Das klappt sogar, solange keine Felder der Klasse verwendet werden. Aber ich wiederhole ja nur das, was hier Andere schon geschrieben haben.

Zurück zum Thema:

Wenn Du mehrere Referenzen auf ein Objekt hast, dann benötigst Du Referenzzähler. COM-Objekte implementieren das, und Delphi unterstützt COM-Objekte. Schau mal in der Hilfe nach, wie mal COM-Objekte deklariert und verwendet, eigentlich ganz einfach.

Prinzipiell geht das so: Bei jeder Referenz auf das Objekt erhöhst Du den Referenzzähler. Anstatt das Objekt freizugeben, rufst Du eine Methode 'Release' auf, in der Du den Zähler wieder erniedrigst. Wenn der Zähler 0 ist, gibst Du das Objekt wieder frei.
Delphi-Quellcode:
Function GetReferenceToMyObject : TMyObject;
Begin
  If not Assigned (PrivateObject) Then
    PrivateObject.Create;
  PrivateObject.IncreaseRefCount;
End;

Procedure TPrivateObject.Release;
Begin
  DecreaseRefCount;
  If fRefCount = 0 Then Free;
End;
Aufpassen beim Anfassen der Refcounts: Am Besten sollte man das noch threadsicher kapseln (Oder, wie gesagt, COMs nehmen).

Khabarakh 8. Nov 2005 19:44

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von alzaimar
Zitat:

Zitat von Khabarakh
Genau. Es wäre ja wirklich sinnlos, wenn in Free Self auf nil geprüft wird, aber schon beim Methodenaufruf wegen Self = nil eine Exception ausgelöst würde.

Free ist aber genauso definiert:
Delphi-Quellcode:
Procedure TObject.Free;
Begin
  If Self<>Nil Then Destroy;
End;

Das ist mir bewusst, die Modi waren nicht unabsichtlich so gewählt :wink: . Es ging ja darum, dass man Methoden auch ohne Instanz aufrufen kann (wäre dies nicht so, würde eben obige if-Verzweigung keinen Sinn, da nie erfüllt, machen).

Sidorion 9. Nov 2005 08:27

Re: Bin ich schon zerstört?
 
Das mit den Referenzzählern geht nicht, weil
a) viel zu viel Aufwand (wird nicht bezahlt) und
b) das schon seine Richtigkeit hat, das das Objekt schon zerstört ist, weil wenn nicht, gäbs an anderer Stelle Probleme.

Der_Unwissende 9. Nov 2005 08:45

Re: Bin ich schon zerstört?
 
Hey,
ich glaube es gibt keinen guten Weg das ohne try ... except ... abzufangen. Aber als Alternative, gibst du die Instanzen selbst frei? Dann solltest du einfach dafür sorgen, dass FreeAndNil aufgerufen wird (oder selbst danach den Variablenzeiger nil setzen). Ok, ich glaube ehrlich gesagt, dass wenn es so einfach wäre du selbst drauf gekommen wärst, aber kann ja sein.
Ansonsten scheinen die anderen Möglichkeiten nicht wirklich zu funktionieren (zumal Referenzzähler ja nicht anzeigen können ob eine Instanz existiert oder nicht).

Gruß Der Unwissende

shmia 9. Nov 2005 09:21

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von Sidorion
Die Instanz wird ganz normal zerstürt, habe aber zwei Zeiger drauf und die wissen voneinander nix.

Das ist für mich der entscheidende Satz.
alzaimar hat dir ja schon vorgeschlagen, Inferfaces und Referenzzählung einzusetzen.

Du erzeugst ein Objekt und speicherst es in mehr als einer (Objekt-)Variablen.
Wenn nun gleichzeitig die Gefahr besteht, dass das Objekt freigeben werden kann aber
die Objekt-Variablen weiterhin leben (also im Scope bleiben), hast du einen Software Designfehler
begangen.
Jeder Versuch, den Fehler mit Try..Except auszubügeln ist Banane.
Entweder gelingt es, beim Freigeben des Objekts ALLE (Objekt-)Variablen auf nil zu setzen,
oder du verwendest Interfacezeiger oder du gehst zurück auf Anfang und überlegst dir was ganz neues.

Phoenix 9. Nov 2005 10:05

Re: Bin ich schon zerstört?
 
Japp, das kann ic´h nur unterschreiben. Wenn Du eine Referenz auf ein bereits zerstörtes Objekt hältst und die von der Zerstörung nichts mitbekommt, dann ist vorher irgendwas schief gelaufen. Du solltest das Design nochmal überdenken und tatsächlich auf Referenzzähler zurückgreifen.

alzaimar 9. Nov 2005 11:29

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von Sidorion
Das mit den Referenzzählern geht nicht, weil
a) viel zu viel Aufwand (wird nicht bezahlt)

So ist das nunmal, wenn man einen 'falschen' Ansatz hat.
Du kannst die Konstruktoren Create und den Destruktor Destroy in '_create' und '_Destroy' umbenennen.
Create wird eine Class function, die Dir das eine Objekt liefert und den ref-zähler hochsetzt.
Destroy wird eine einfache Methode, die den RefCount wieder runterzählt und ggf das interne Objekt freigibt
Aufwand: 10 minuten (wenns klappt :mrgreen: ). Sollte aber, sofern Du sauber programmiert hast. Wenn nicht, goto a)

DerDan 9. Nov 2005 12:07

Re: Bin ich schon zerstört?
 
das find ich persönlich komisch:


erst um Hilfe fragen und dann aber doch nix ändern wollen.

Das erinnert mich an meinen Chef der sagt auch immer:

"beheb mal den Fehler, änder aber um himmels willen nichts am code"

und da steh ich dann...



mfg

MaBuSE 9. Nov 2005 12:40

Re: Bin ich schon zerstört?
 
Zitat:

Zitat von DGL-luke
Delphi-Quellcode:
if assigned(myobj) then
 dosomething;
wenn der zeiger anständig nil gesetzt wurde nach dem zerstören.

Zitat:

Zitat von Sidorion
@DGL-Luke: darum ist Assigned(MyObj) in dem Falle nach wie vor true, das ist ja das Problem. Wenn nicht, würd ich die Methode ja nicht mehr rufen.

Das ist das was er mit "wenn der Zeiger anschtändig nil gesetzt wurde" meinte.

Ich habe mir angewöhnt Objekte immer wie folgt freizugeben:
Delphi-Quellcode:
...
  myObject.Free;
  myObject := nil;
...
Dann funktioniert auch der oben angegebene Code wieder. (asigned prüft auf nil)

Free prüft auch auf nil, deshalb ist folgender Code fehlerhaft:
Delphi-Quellcode:
...
  myObject := TmyObject.Create;
...
  myObject.Free;

  myObject.Free; // hier gibts Exception !!!
...
Folgender code läuft hingegen fehlerfrei:
Delphi-Quellcode:
...
  myObject := TmyObject.Create;
...
  myObject.Free;
  myObject := nil;

  myObject.Free;   // kein Problem, da destroy nur aufgerufen wird, wenn <> nil
  myObject := nil; // diese 2. nil-Zuweisung ist überflüssig,
                    // aber ich kombiniere immer Free mit nil Zuweisung ;-)
...

Wenn Dir das zuviel Tipparbeit ist, kannst Du auch FreeAndNil verwenden.
Delphi-Quellcode:
...
  FreeAndNil(myObject);
...
(ist in SysUtils definiert)
Delphi-Quellcode:
{ *********************************************************************** }
{                                                                         }
{ Delphi / Kylix Cross-Platform Runtime Library                          }
{ System Utilities Unit                                                  }
{                                                                         }
{ Copyright (c) 1995-2002 Borland Softwrare Corporation                  }
{                                                                         }
{ *********************************************************************** }

unit SysUtils;
...
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;
...

SirThornberry 9. Nov 2005 16:54

Re: Bin ich schon zerstört?
 
@Mabuse: Das mit FreeAndNil bringt ihn aber auch nicht weiter da er ja zwei variablen hat wo die Instanz drauf gespeichert ist. Und wenn er für eine Variable FreeAndNil aufruft ist die zweite trotzdem noch nicht nil.
Eine weitere Lösung wäre keinen einfachen Instanzzeiger (also nur ne Variable) zu nehmen sonder ein eigenes Object zu bauen. Dieses Object muss sich beim Create bei der Componente eintragen die später zerstört wird. Wenn jetzt deine Componente zerstört wird kannst du alle Objectreferenzen durchgehen die in der Liste mitgeführt wurden und diese auf nil setzen.
Oder noch einfacher. Du übergibst der Componente die später zerstört wird die Adresse von der Instanzvariablen. Die Komponente muss diese Adressen natürlich in einer Liste speichern. Und beim Destroy setzt es an all diesen Adressen die Werte auf nil.

Hier ein Beispiel (für TButton):
Delphi-Quellcode:
  TButton = class(StdCtrls.TButton)
  private
    fRefList: TList;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure AddInstancePointer(var AInstance: TButton);
    procedure RemoveInstancePointer(var AInstance: TButton);
  end;
[...]
constructor TButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fRefList := TList.Create;
end;

destructor TButton.Destroy;
var LCount: Integer;
begin
  for LCount := 0 to fRefList.Count - 1 do
    Pointer(fRefList.Items[LCount]^) := nil;
  fRefList.Free;
  inherited Destroy;
end;

procedure TButton.AddInstancePointer(var AInstance: TButton);
begin
  fRefList.Add(@AInstance);
  AInstance := Self;
end;

procedure TButton.RemoveInstancePointer(var AInstance: TButton);
var LIndex: Integer;
begin
  LIndex := fRefList.IndexOf(@AInstance);
  if LIndex > -1 then
    fRefList.Delete(LIndex);
end;
und hier wie man es anwendet (mit 2 Referenzen):
Delphi-Quellcode:
var LInstance: TButton;
begin
  LInstance := TButton.Create(Self);
  LInstance.AddInstancePointer(fZusatzInstance1);
  LInstance.AddInstancePointer(fZusatzInstance2);
[...]
Somit zeigen die beiden zusätzlichen Instancen (fZusatzInstance1 und fZusatzInstance2) nun auch auf das Object (LInstance).

Wenn das Object anschließend durch "fZusatzInstance1.Free" oder "fZusatzInstance2.Free" feigegeben wird, so sind automatisch fZusatzInstance2 und fZusatzInstance1 auf "nil" gesetzt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:58 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz