Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Referenzen auf ungültige Objekte (https://www.delphipraxis.net/159095-referenzen-auf-ungueltige-objekte.html)

Furtbichler 3. Nov 2011 21:19

AW: Referenzen auf ungültige Objekte
 
Haltet ihr es nicht für ein 'design flaw', wenn ein Objekt von unbestimmt vielen anderen Objekten referenziert wird, während wieder irgend jemand meint, das Objekt auf den Müll werfen zu wollen?

Bei meiner Art zu programmieren (nicht das die besonders toll wäre), kommt so etwas einfach nicht vor. Ich kann mir das nur bei dem OOP-Pendant einer Lookupliste vorstellen. Hier würde ich aber eher die ID speichern (von mir aus der Index in die Liste). Aber selbst da hätte ich ein ungutes Gefühl, denn wenn irgendwer auf ein Objekt zugreift, das ihm von jemandem anderen unterm Ar*** weggezogen wird... also ich weiss nicht, irgendwie "unsauber".

Oder übersehe ich etwas?

Sir Rufo 3. Nov 2011 21:45

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von Furtbichler (Beitrag 1134381)
Oder übersehe ich etwas?

Ja, stell dir einfach mal vor, du hast (wie hier schon von stahli skizziert) eben so ein Person-Objekt, welches sich an mehreren anderen Objekten befinden kann (die Person ist Spieler, Schiedsrichter, Sponsor, Depp für Alles ;) )
An einer Stelle im Programm änderst du jetzt den Namen der Person von "Meier" auf "Müller".
Dann wäre es doch schön, wenn alle, die auf die gleiche Person referenzieren auch sofort "Müller" anzeigen und nicht noch den alten Stand mit "Meier".

Wenn du überall eine eigene Instanz benutzt, dann hast du eben das Problem alle Referenzen zu finden und zu aktualisieren.
Der Aufwand ist gleich, nur dass es eben nicht knallt.

Dafür gibt es aber halt die Interfaces ... da wird eben nicht freigeben, sondern es gibt sich selber frei.
Ein Problem bleibt aber, und das ist, wenn die Person tatsächlich aus dem gesamten Programmumfeld gelöscht werden soll, also die Person "Müller" gibt es nicht mehr.

Dann hilft der von mir beschriebene kombinierte Ansatz mit der ReferenzID und Interfaces

himitsu 3. Nov 2011 21:51

AW: Referenzen auf ungültige Objekte
 
Solange immer nur Einer gleichzeitig für die Freigabe verantwortlich ist, ist es vollkommen egal, wieviele Referenzen auf ein Objekt zeigt.
Es sollte nur sichergestellt werden, daß keine fremden Referenzen mehr in Umlauf sind, sobald das Objekt freigegeben wird.


Zitat:

haltet ihr es nicht für ein 'design flaw', wenn ein Objekt von unbestimmt vielen Anderen Objekten referenziert wird,
Das ist eigentlich die Natur von Objektzeigern, denn sonst könnte man die nichtmal als Parameter weitergeben, wenn es nur eine Referenz geben dürfte.

Zitat:

während wieder irgend jemand meint, das Objekt auf den Müll werfen zu wollen?
Aber das sollte/muß man wirklich unterbinden. Und sei es auch dadurch, daß man den anderen irgendwie mitteilt, daß dieses Objekt nun verschwindet.

Bummi 3. Nov 2011 22:25

AW: Referenzen auf ungültige Objekte
 
ketzerischer Ansatz: wenn niemand den Zeiger auf das Objekt verwenden würde sondern einen Zeiger auf den Zeiger (um den sich der Destruktor des Objekts selbst kümmern muss) gäbe es immer einen gültigen Zugriff und ein nil würde sauber erkannt werden.
Teile von dem was Sir Rufo beschrieben hat würde ich als Observer sehen.

stahli 3. Nov 2011 22:41

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von NamenLozer (Beitrag 1089530)
Ich hab mal gerade was zusammengehackt... mit Generics ließe sich da sicher noch mehr machen.

Leider scheint mir das doch nicht zu helfen, da ich damit m.E. keine Referenzierungen meiner Objekte untereinander verwalten kann.


Zitat:

Zitat von Sir Rufo (Beitrag 1134363)
Diese GlobaleObjektListe gibt die Instanzen allerdings nicht frei, sondern vergisst diese einfach nur (IPerson => Interface).
Dann sollte es nicht mehr rumsen und die Referenzen sind sauber.
Und dieses Rumgeeiere mit RTTI kannst du dir sparen ;)

Eine solche (ähnliche) Liste habe ich, um auf gültige Objekte prüfen zu können.

Delphi-Quellcode:
function odExist(od: Tod): Boolean;
begin
  if od = nil then
    Exit(False);
  Result := odList.IndexOf(od) >= 0;
end;
Ansonsten will ich aber lieber die Objekte "physisch" vorliegen haben als sie bei jedem Zugriff aus einer Liste herauszusuchen.


Zitat:

Zitat von Furtbichler (Beitrag 1134381)
Haltet ihr es nicht für ein 'design flaw', wenn ein Objekt von unbestimmt vielen anderen Objekten referenziert wird, während wieder irgend jemand meint, das Objekt auf den Müll werfen zu wollen?
Bei meiner Art zu programmieren (nicht das die besonders toll wäre), kommt so etwas einfach nicht vor. Ich kann mir das nur bei dem OOP-Pendant einer Lookupliste vorstellen. Hier würde ich aber eher die ID speichern (von mir aus der Index in die Liste). Aber selbst da hätte ich ein ungutes Gefühl, denn wenn irgendwer auf ein Objekt zugreift, das ihm von jemandem anderen unterm Ar*** weggezogen wird... also ich weiss nicht, irgendwie "unsauber".
Oder übersehe ich etwas?

Hier unterstelle ich mal, dass Du nicht die Referenzen für problematisch hältst, sondern, dass plötzlich ein Objekt aufgelöst wird, ohne dass vorher alle Referenzen aufgelöst werden...
Aber darum geht es ja gerade. Natürlich verhindere ich z.B., dass ein Spieler gelöscht wird, der in einem Spiel spielt o.ä.
Wenn aber ein Objekt aufgelöst wird will ich erreichen, dass alle Referenzen zuverlässig auf nil gesetzt werden. Das soll weitestgehend automatisch erfolgen (ohne dass ich das in meinem eigentlichen Projekt explizit regeln muss.) Gleiches gilt im übrigen auch für das DataBinding. Wenn ein odEdit meinen Person.FirstName anzeigt und Person aufgelöst wird, darf nix knallen und das edit muss einen Leerstring anzeigen (macht es ja auch ;-)) Auch der Schiedsrichter, Spieler oder Bierverkäufer (nicht Depp!), der die Person referenziert (genau was Sir Rufu später meinte) darf in solch einem Fall nicht problematisch reagieren, sondern soll erkennen, dass die Person nun nil ist. OB die Person gelöscht werden darf, gehört auf ein anderes Blatt.


Zitat:

Zitat von himitsu (Beitrag 1134387)
Solange immer nur Einer gleichzeitig für die Freigabe verantwortlich ist, ist es vollkommen egal, wieviele Referenzen auf ein Objekt zeigt.
Es sollte nur sichergestellt werden, daß keine fremden Referenzen mehr in Umlauf sind, sobald das Objekt freigegeben wird.

Ich habe es jetzt nach dem Observer-Pattern ohne Interface-Einsatz gelöst. Da meine Objekte von einem Experten erstellt werden macht das praktisch für mich keinen Mehraufwand.
Es funktioniert jetzt sehr gut (konnte bisher keine Probleme finden) und auch recht schnell. Die RTTI setze ich aber weiter ein um "PropertyByName" zu realisieren (auch wenn das leider noch etwas langsam ist).

Hier mal ein paar Schnipsel:

Delphi-Quellcode:
Pattern-Auszug:
...
 -> normal                               // normale Eigenschaften
function {CLASSNAME}.get_[N]: [T];
begin
  Result := F[N];
end;

procedure {CLASSNAME}.set_[N](const Value: [T]);
begin
  if F[N] <> Value then
  begin
    F[N] := Value;
    Changed;
  end;
end;

 -> pointer, pointeras                  // Wenn Eigenschaften Objekte referenzieren
function {CLASSNAME}.get_[N]: [T];
begin
  Result := F[N];
end;

procedure {CLASSNAME}.set_[N](const Value: [T]);
begin
  if F[N] <> Value then
  begin
    if Assigned(F[N]) then
      F[N]._RemoveRef(Self);            // Überwachung abmelden
    F[N] := Value;
    if Assigned(F[N]) then
      F[N]._AddRef(Self);               // Überwachung anmelden
    Changed;
  end;
end;
...

Delphi-Quellcode:
destructor Tod.Destroy;
var
  iod: Tod;
begin
  if odRoot = Self then
    odRoot := nil;
  odList.Extract(Self);
  if Assigned(FRefList) then
    begin
      for iod in FRefList do
        begin
          if odExist(iod) then
            begin
              odProp.ClearPointer(iod, Self); // Objekt untersuchen und Referenzen auf Self "nilen"
            end;
        end;
      FreeAndNil(FRefList);
    end;
  odRemoveRefList(Self); // Self aus fremden RefListen löschen
  inherited; // jetzt kann das Objekt freigegeben werden
//
// Es wäre halt schön, wenn die Freigabe auch in Objekten und Variablen erfolgen würde
// die nicht unter der obigen Kontrolle (also in den von mir verwalteten Listen) stehen.
// Das müsste der Compiler dann selbständig verwalten und ich könnte mir das Geraffel sparen.
// Außerdem wäre wünschenswert, dass "Self" auf nil gesetzt wird (wie auch immer das der
// Compiler realisieren könnte).
//
end;

procedure Tod._AddRef(od: Tod);
begin
  if not Assigned(FRefList) then
    FRefList := TObjectList<Tod>.Create(False);
  FRefList.Add(od);
end;

procedure Tod._RemoveRef(od: Tod);
begin
  FRefList.Remove(od);
  if FRefList.Count = 0 then
    FreeAndNil(FRefList);
end;

procedure Tod._RemoveFromRefList(od: Tod);
begin
  if Assigned(FRefList) then
    FRefList.Remove(od);
end;

----------------------

procedure odRemoveRefList(od: Tod);
var
  iod: Tod;
begin
  for iod in odList do
    iod._RemoveFromRefList(od);
end;

function odExist(od: Tod): Boolean;
begin
  if od = nil then
    Exit(False);
  Result := odList.IndexOf(od) >= 0;
end;

Zitat:

Zitat von Bummi (Beitrag 1134391)
ketzerischer Ansatz: wenn niemand den Zeiger auf das Objekt verwenden würde sondern einen Zeiger auf den Zeiger (um den sich der Destruktor des Objekts selbst kümmern muss) gäbe es immer einen gültigen Zugriff und ein nil würde sauber erkannt werden.

Hmm, aber dann dürfte man den Zeiger, der auf den Zeiger zeigt nicht mehr einfach freigeben... Lyncht Ihn! :mrgreen:

Bummi 3. Nov 2011 22:57

AW: Referenzen auf ungültige Objekte
 
Zitat:

Hmm, aber dann dürfte man den Zeiger, der auf den Zeiger zeigt nicht mehr einfach freigeben... Lyncht Ihn!
Danke ... aber das wäre in meinem Modell die unterste Schicht, jedes Objekt das erzeugt wird lässt sich von nennen wir es Manager eine eindeutige Nummer zuweisen, übergibt dem Manager seine Adresse und teilt dem Erzeuger statt der Objektinstanz die Nummer mit, im Destroy wendet er sich ebenfalls an den Manager, das sich im Gedankenspiel alle auf den Manager beziehen würden sollte es keine toten Referenzen mehr geben.

stahli 3. Nov 2011 23:12

AW: Referenzen auf ungültige Objekte
 
Ja , das wäre dann der Ansatz wie von Sir Rufo - es werden keine festen Referenzen auf Objekte verwaltet - oder?
Aber in dem Zusammenhang bilden die Objekte nicht direkt die Datenstruktur ab (Owner und SubObjekte). Oder verstehe ich Dich da falsch?

Bei meiner Lösung kann ich leicht die Mannschaft suchen, in der sich ein Spieler befindet (über die Owners iterieren).
Du müsstest im Spieler eine MannschaftsId verwaltet, wenn ich das richtig interpretiere.

Vermutlich könnte man Deine Lösung aber leichter für eine Datenbankverwendung ausbauen.

himitsu 3. Nov 2011 23:42

AW: Referenzen auf ungültige Objekte
 
Wozu eine eindeutige Nummer?

Delphi-Referenz durchsuchenTObject.GetHashCode und per Default gibt diese Methode den Objektzeiger zurück, denn der Zeigerinhalt, bzw. die Speicheradresse eines Objektes sind immer eindeutig, da im RAM immer nur ein Objekt n der selben Stelle liegen kann.
Also bracucht sich der Manager nur die Objektreferenzen merken, anstatt zusätzlich noch jeweils eine ID.

Der Manager kann somit, am Ende, auch locker eine einfache TObjectList sein.

Bummi 4. Nov 2011 06:08

AW: Referenzen auf ungültige Objekte
 
@himitsu

ich sehe noch nicht was das helfen soll
Delphi-Quellcode:
var
  b:TButton;
  hc:Integer;
begin
  b := Button2;
  hc := b.GetHashCode;
  Caption := IntToStr(Integer(b)) + '-'+IntToStr(Integer(Button2)) + '-'+IntToStr(hc);
  FreeAndNil(Button2);
  Showmessage(IntToStr(Integer(b)) + '-'+IntToStr(Integer(Button2)) + '-'+IntToStr(hc));
  Showmessage(IntToStr(b.GetHashCode));// das hier müsste eigentlich schon knallen
end;
Wenn statt dessen über einen Zwischenzeiger, der über eine Nummer/GUID was auch immer im Manager, das kann durchaus eine Liste(Anrede,Objektzeiger), angesprochen und geprüft wird, die Einträge dort vom Objekt beim Erzeugen/Vernichten hinzugefügt/entfernt werden kann so etwas nicht passieren.

@stahli

Die Owner/Parent was auch immer Struktur bräuchte IMHO nicht verändert werden, in Deinem sp:TSpieler müsste einfach transparent die Managernummer abgelegt werden, alle Zugriffe müssten dann ebenfalls transparent über den Manager abgewickelt werden. Das wäre ein kompletter Sprachumbau, könnte dann aber völlig transparent wie bisher laufen. Zugriffe wie @MySpieler.Irgendwas würden dann freilich nicht mehr funktionieren.

Furtbichler 4. Nov 2011 06:24

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von Sir Rufo (Beitrag 1134383)
Zitat:

Zitat von Furtbichler (Beitrag 1134381)
Oder übersehe ich etwas?

Ja, stell dir einfach mal vor, du hast (wie hier schon von stahli skizziert) eben so ein Person-Objekt, welches sich an mehreren anderen Objekten befinden kann (die Person ist Spieler, Schiedsrichter, Sponsor, Depp für Alles ;) )
An einer Stelle im Programm änderst du jetzt den Namen der Person von "Meier" auf "Müller".
Dann wäre es doch schön, wenn alle, die auf die gleiche Person referenzieren auch sofort "Müller" anzeigen und nicht noch den alten Stand mit "Meier".

Das würde ich über Notifications lösen, und wenn ich schon dabei bin, das Freigeben gleich mit. So funktioniert ja auch TDatasource.

stahli 6. Nov 2011 19:12

AW: Referenzen auf ungültige Objekte
 
Nachtrag zum Beitrag #45, falls es mal jemand nachbaut...
Es ist noch folgende Änderung notwendig:

Delphi-Quellcode:
procedure Tod._RemoveRef(od: Tod);
begin
  if (csDestroying in ComponentState) then
    Exit;
  if not Assigned(FRefList) then
    Exit;
  FRefList.Remove(od);
  if FRefList.Count = 0 then
    FreeAndNil(FRefList);
end;

procedure Tod._RemoveFromRefList(od: Tod);
begin
  if (csDestroying in ComponentState) then
    Exit;
  if Assigned(FRefList) then
    FRefList.Remove(od);
end;
(Ich kann den Beitrag leider nicht mehr editieren.)

Thom 2. Mär 2012 20:44

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von Thom (Beitrag 1089247)
Zitat:

Zitat von stahli (Beitrag 1089229)
Jeder Anfänger geht doch erst mal davon aus, dass nach
Delphi-Quellcode:
O := TObject.Create;
O1 := O;
O.Free;
O1 = nil ist.

[...]

Es gibt viele Lösungsmöglichkeiten, das von Dir beschriebene Problem anzugehen (viele davon wurden schon diskutiert).
Aber mich ausschließlich auf den Compiler verlassen!? Nicht wirklich.

Mal ein Gegenbeispiel für den Aufwand, der entstehen würde:
Eine Objektreferenz wird in einer TObjectList gespeichert. Diese wird aufgelöst, bevor das Objekt freigegeben wird. Und nun!? Jetzt müßte Code generiert werden, wie und ob auf die Objektreferenz überhaupt noch zugegriffen werden kann.

Ich denke, der einfachste und sicherste Weg ist eine saubere Programmierung im Einzelfall - auf für weniger anspruchsvolle Programmierer wie mich... 8-)

Ein knappes Jahr später kann ich dazu nur noch sagen: :wall:

Du hast vollkommen Recht, stahli: Es gibt durchaus sinnvolle Anwendungsmöglichkeiten für ein derartiges Verhalten und ich habe inzwischen auch Situationen gefunden, in denen das sogar zwingend notwendig ist.
Inzwischen funktioniert es auch:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  o, o1: INotify;
begin
  o:=TNotifyObject.Create;
  o1:=o;
  o.Free;
  if not assigned(o) and not assigned(o1)
    then ShowMessage('Toll!')
    else ShowMessage('War wohl nichts...');
end;
Natürlich erscheint die erste Meldung. :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:51 Uhr.
Seite 2 von 2     12   

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