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.
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.
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.
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;
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!