![]() |
Intelligente Objekte - automatische Freigabe von Referenzen
Hiermit möchte ich ein Konzept zur automatischen Freigabe von Referenzen auf nicht mehr vorhandene Objekte zur Diskussion stellen.
Über das automatische Rücksetzen von Referenzen wurde schon viel diskutiert. Einer der Threads zu diesem Thema ist stahli's ![]() Jedem ist (sollte) bekannt (sein), daß nach
Delphi-Quellcode:
var
o1, o2: TObject; begin [...] o1:=TObject.Create; o2:=o1; o1.Free; [...] end; Listing 1
Delphi-Quellcode:
var
o1, o2: TObject; begin [...] o1:=TObject.Create; o2:=o1; FreeAndNil(o1); [...] end; Listing 2 Obwohl es zu diesem Thema meistens negative Meinungen gibt (braucht man nicht, Blödsinn, zeugt von einem schlechten Design, ...), existieren Fälle, in denen das automatische Setzen von ungültigen Referenzen auf nil von entscheidender Bedeutung ist. Dazu ein praktisches Beispiel mit dem ![]()
Delphi-Quellcode:
TForm1 = class([...])
[...] private FMyMarker: TMarker; //Stelle 1 [...] end; procedure TForm1.Button1Click(Sender: TObject); begin with Script do begin FMyMarker:=New(Google.Maps.Marker); //Stelle 2 FMyMarker.Position:=New(Google.Maps.Point(10,20)); FMyMarker.Map:=Maps[0]; end; end; Listing 3 Werden jetzt aber massenhaft neue Marker angelegt, steigt der Speicherverbrauch duch die Punkte, obwohl sie eigentlich gar nicht mehr benötigt werden. Abhilfe würde eine "ordentlichere" Programmierung im Delphi-Stil schaffen:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var Point: TPoint; begin with Script do begin Point:=New(Google.Maps.Point(10,20)); try FMyMarker:=New(Google.Maps.Marker); FMyMarker.Position:=Point; FMyMarker.Map:=Maps[0]; finally Point.Free; end; end; end; Listing 4
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var Point: IPoint; begin with Script do begin Point:=New(Google.Maps.Point(10,20)); FMyMarker:=New(Google.Maps.Marker); FMyMarker.Position:=Point; FMyMarker.Map:=Maps[0]; end; end; Listing 5 Das Marker-Objekt würde so aber - wenn es nicht in FMyMarker (vom Typ IMarker) referenziert würde - sofort nach Beendigung der Methode Button1Click wieder freigegeben und so von der Karte verschwinden. Um da zu vermeiden, muß die Liste TScript.Markers Interfaces verwenden. Dadurch ergibt sich aber eine äußerst ungünstige Situation: Eine Zuweisung von nil zu FMyMarker bewirkt augenscheinlich gar nichts und erst das zusätzliche Löschen mit TScript.Markers.Delete(...) oder TScript.Markers.Remove(...) gibt den Marker tatsächlich frei:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin with Script do begin Markers.Remove(FMyMarker); FMyMarker:=nil; end; end; Listing 6 Wäre es nicht bedeutend einfacher - zusätzlich zu den Möglichkeiten, die die Referenzzählung über ein Interface bietet -, ein Objekt explizit freigeben zu können - unabhängig davon, wieviele Referenzen noch bestehen -, als dessen Folge das Objekt aktiv alle Verweise auf nil setzt und sich in allen Listen abmeldet?
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin FMyMarker.Free; end; Listing 7 Anders herum entfernt
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin Script.Markers[...].Free; //oder Script.Markets.Delete(...); //oder Script.Markers.Remove(...); end; Listing 8 Das wäre ein erheblicher Fortschritt, würde die Vorteile der Verwendung von reinen Objekten mit denen von Interfaces verbinden und die Möglichkeiten sogar noch erweitern. Über derartige intelligente Zeiger und Objekte wurde schon einiges geschrieben - allerdings setzen alle mir bekannten Lösungen für Delphi auf die Verwendung von Generics und/oder anonymen Methoden und schließen damit ältere Compiler aus. Das Delphi Framework für Google Maps soll aber auch weiterhin ab Delphi 5 verwendbar sein. Deshalb wurde folgendes Interface entworfen:
Delphi-Quellcode:
type
INotify = interface(IInterface) ['{D37C5177-D900-4D99-A97B-A341865B258D}'] procedure AddRef(const Ref); procedure AddNotify(const Notify: INotify); procedure Free; procedure FreeNotify(const Notify: INotify); procedure RemoveRef(const Ref); procedure RemoveNotify(const Notify: INotify); function _ReleaseSave: Integer; function GetObject: TObject; function GetRefCount: Integer; function IsDestroying: Boolean; property RefCount: Integer read GetRefCount; end; Listing 9
Wird das Objekt, das INotify implementiert, freigegeben, informiert es alle angemeldeten Schnittstellen über FreeNotify und setzt alle Speicherreferenzen auf nil. Die Funktion GetObject unterstützt ältere Compiler, die noch keinen Interface-to-Object-Cast besitzen. IsDestroying liefert true, sobald sich das Objekt hinter dem Interface in der Methode Destroy befindet. Das ist notwendig bei Listen - genauer gesagt bei TObjectList -, um einen Mehrfachaufruf des Destructors zu vermeiden. Zu erwähnen wäre noch _ReleaseSave: Damit wird das unangenehme Verhalten von TInterfacedObject vermieden, daß das Objekt nach Abfrage des Interfaces gleich wieder zerstört wird, wenn vorher der Referenzzähler nicht erhöht wurde:
Delphi-Quellcode:
procedure TForm1.Button3Click(Sender: TObject);
var o: TInterfaceObject; begin o:=TInterfacedObject.Create; if Supports(o,IInterface) then ; o. ...; //<- geht schief, da das Objekt inzwischen freigegeben wurde end; Listing 10
Delphi-Quellcode:
procedure TForm1.Button4Click(Sender: TObject);
var o1, o2: INotifyObject; begin o1:=TNotifyObject.Create; o2:=o1; o1.Free; //sowohl o1 als auch o2 sind jetzt nil!!! end; Listing 11
Delphi-Quellcode:
procedure TForm1.Button5Click(Sender: TObject);
var o: TNotifyObject; l: TList; begin o:=TNotifyObject.Create; o.AddRef(o); //<- bei Freigabe Variable auf nil setzen l:=TList.Create; try l.Add(o); o.Free; //Abmeldung bei der Liste und o auf nil setzen ShowMessage(IntToStr(l.Count)); if not assigned(o) then ShowMessage('o=nil'); finally l.Free end; end; Listing 12 Die Methode Free des Objektes kann auch bei nil ausgeführt werden, da sie immer existiert und erst bei der Ausführung getestet wird, ob das Objekt vorhanden ist. Im Gegensatz dazu führt der Versuch, die Interface-Methode bei nil aufzurufen, zu einer Zugriffsverletzung. Geplant ist, diese Technik in der kommenden Version der Frameworks einzusetzen. Im Gegensatz zu stahli's Lösung ist sie aber so allgemein gehalten, daß alle Objekte, die das INotify-Interface unterstützen oder einfach von TNotifyObject abgeleitet werden, an diesem Mechanismus teilhaben können. Die wichtigsten Listen TList, TObjectList, TThreadList und TInterfaceList wurden mit dieser Schnittstelle ausgerüsten und stehen so allgemein zur Verfügung. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Die Lösung funktioniert und ist elegant, keine Frage, aber Ich kann mir nicht helfen: Ich sehe darin einen Versuch, schlechtes Design nachträglich zu vertuschen.
Es ist doch so: Wenn ich in einer Klasse einen Verweis auf ein externes Objekt habe, das jederzeit freigegeben werden könnte, dann breche ich mir doch einen ab, Code für diesen Verweis zu schreiben, weil ich jederzeit und immer befürchten muss, das mir der Zeiger unterm Arsch weggezogen wird. Sicheren Code kann ich ohne zusätzliche Verwendung von Critical Sections so nicht schreiben:
Delphi-Quellcode:
Mit weniger Code auskommen heißt nicht, es besser zu machen. 'Kompakter' Code ist nicht gleichbedeutend mit wartbar, lesbar oder robust. In meinen Augen ist es der Lesbarkeit nicht dienlich, solche Seiteneffekte zu implementieren. Ohne zusätzliche Aktionen ist o.g. Code zwar augenscheinlich sicher, aber der Fehler ist nur sehr schwer zu lokalisieren ("Wieso ist der Pointer auf einmal nil? Ich hab doch extra eine Abfrage eingebaut?")
If Assigned (FMyMarker) Then // <--- klappt noch
// hier wird der Marker freigegeben FMyMarker.Bar(); // <--- PENG In diesem Fall würde ich mit einem Handle arbeiten, und bei der Verwendung eines Markers explizit ein (Singleton-)Objekt anfordern, die Arbeiten ausführen und dann, wenn ich fertig bin, wieder zurückgeben, ähnlich einer TThreadList. Das bedeutet, das der Code innerhalb der Anforderung/Rückgabe 100% sicher ist.
Delphi-Quellcode:
Wer nun meint, das das mit dem Try..Finally schlecht(er) lesbar ist, der refaktorisiert das eben in eine lokale Methode mit aussagekräftigem Namen.
MyMarker := MyGoogleContainer.LockObject(FMyMarkerHandle);
If Assigned(MyMarker) Then Try MyMarker.Foo(); Finally MyGoogleContainer.Unlock(MyMarker); End; Die entgültige Freigabe des Objektes obliegt dem Erzeuger, hier vermutlich dem Skript-Objekt oder dem Google-Maps Framework. Der Verwender des Frameworks darf natürlich explizit die Marker entgültig freigeben, allerdings nicht direkt, sondern über eine Methode des Frameworks ('ReleaseMarker'); Eine anschließende Anforderung eines (mittlerweile ungültigen) Handles liefert ein NIL-Objekt zurück, auf das ich explizit und gewollt reagieren kann. Aber vielleicht habe ich das entscheidende übersehen: Wie löst Du das Problem des 'unterm Arsch wegziehen'? |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Ich würde das Ding, ganz einfach nur als Strohdoofes und ganz normales Interface auslegen.
Es wird freigegeben wenn alle Referenzen auf nil stehen und es verändert nicht von innen her seine Referenzen. Wenn der Punkt nicht mehr sichtbar sein soll, wird er nur aus den Googleobjekten/-listen rausgenommen und maximal in den Punkt-Objekt noch ein Flag gesetzt "ich bin gelöscht", welches man auswerten könnte, wenn doch noch jemand mal auf eine Methode des Objektes zugreifen will, obwohl es "eigentlich" nicht mehr existiert, bzw. nicht mehr angezeigt wird. Problem: Man markiert eine Speicherposition 'ner lokalen Variable, die Prozedur wurde schon verlassen und Variable existiert nicht mehr. Oder man markiert ein Feld in einem Objekt, gibt das Objekt frei und vergist die Markierung zu entfernen. Wird jetzt das Objekt freigegeben und werden dann alle markierten Referenzen auf nil gesetzt, dann würden "falsche" Speicherbereiche überschrieben. Ich wünsche dem armen Kerl/Mädl schonmal viel Spaß, welche(r) diesen Bug dann suchen darf, wo sich unvorhersehbar irgendwo etwas verändert, welches quasi einem Bufferoverrun ähnelt. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Sowas passiert halt, wenn man Code aus einer GC Sprache 1 zu 1 in Delphi übersetzt ;)
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
In Delphi haben wir ja auch sowas wie einen GC und das nennt sich Interface (String und dyn. Array gehört auch mit dazu).
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Für COM mag das zwar einen gewissen Sinn haben, aber Interfaces in einer objektorientierten Sprache immer per Ref-Count zu verwalten ist im allgemeinen grober Unfug. Auf Design-Fehlern dann irgendwelche Framework-Spezialitäten aufzubauen halte ich für äußerst fragwürdig... |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Blöde Frage: Woher weiß ein GC, wann ein Objekt nicht mehr benötigt wird?
Nur durch den Scope? Oder wird nicht auch hier eine Art Referenzzähler verwendet? Außerdem verstehe ich nicht, was an Referenzzählern grober Unfug sein soll. Ich dachte immer, das die Strings genauso verwaltet werden. In diesem Fall wäre das zumindest eine sichere Alternative. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Der GC merkt sich nicht nur die Anzahl der Referenzen, sondern auch die Addressen der Referenzen selber ... er weiß also wo alle Referenzen liegen.
In diesem Sinne ist eine "billige" Referenzzählung wesentlich einfacher. > nur ein Integer, anstatt einer rießigen Liste Und nein, bei Interfaces müssen die referenzen gezählt werden, sonst weiß man ja nicht wann es keine mehr gibt, um das Interface dann zreitugeben. Aber ja, auch interfaces kann man ohne Referenzzählung nutzen. Siehe TComponent. Denn dieses kann man auch über ein Interface ansprechen, allerdings ohne Zählung, da dort das Objekt nicht über die Interfacereferenzen freigegeben wird, sonder über Free, denn sonst würde das Objekt schon gelöscht, wenn die letzte Interfacereferenz weg ist, aber irgendwo könnten dann "ungültige" Objektreferenzen rumliegen. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Das Konzept beim Einsatz von Interfaces ist schon etwas vielschichtiger, so dass der Aspekt des RefCounting nur einer von vielen ist. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Fakt bei Delphi Interfaces sind folgende Dinge (zumindest meinem Verständnis und meinen Erfahrungen nach):
Behält man das jederzeit im Hinterkopf, kann man eine Menge Freude an Interfaces haben - die Nichtbeachtung birgt eine große Anzahl an möglichen Problemen. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Kommt in meinem Code nicht wirklich oft vor, daß ein Objekt zwei Variablen (V1,V2) zugeordnet ist und aus dem Grund eine (V1) auf NIL steht und die andere (V2) nicht.
Ich habe da aber keinen großen Aufwand getrieben. Eine TObjectlist angelegt und jedes erzeugte Objekt in die TObjectlist geschrieben. Beim freigeben aus der TObjectlist wieder herausgelöscht. Wenn ich wissen wollte, ob das Objekt noch vorhanden ist, dann einfach in der Objektliste nachgeschaut. Ganz sicher ist diese Lösung natürlich nicht, denn Wenn ein Objekt (V1) freigegeben wird und ein Objekt erzeugt wird, was zufällig die gleiche Speicheradresse wie V2 hat, dann zeigt V2 zwar auf ein gültiges Objekt, aber eben nicht auf das ursprüngliche. Ist bisher aber nie passiert ;-) |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Sobald man aber ein wenig tiefer in die Objektorientierung eintaucht und es mit diversen Design-Patterns zu tun bekommt wird das so nicht mehr funktionieren. Dieses "nicht mischen" von Objekt und Interface-Referenzen ist eine blöde Idee die vermutlich Leute erfunden haben, die mit aller Gewalt die Design-Fehler von Delphi als "as designed" verteidigen wollten. (Kommt das von Joanna Carter mit ihrer unsinnigen Goldenen Regel?!?). Im allgemeinen kastriert man sich damit die Anwendungsfälle von Interfaces so gewaltig weg, dass es so richtig wehtut. Warum sollte z.B. ein Objekt das ein Interface unterstützt, mit dem ein Logger alle paar Tage mal ein paar Daten auslesen kann sich plötzlich automatisch zerstören nur weil gerade mal kein Logger an den Daten interessiert ist (RefCount Logger-Interface = 0)?! Punkt 2: Es ist auf jeden Fall richtig, dass man nicht verschiedene Arten des Memory-Managements für dasselbe Objekt mischen sollte. Ref-Counting ist für reine Daten-Typen ganz gut und für Objekte nur bei ganz simplen Objekt-Graphen sinnvoll. Punkt 3: Wenn man sich auf Ref-Counting einläßt muß man halt darunter leiden... Wenn man das Ref-Counting wie in TComponent so gut es geht abschaltet, kann man sich auch auf etwas kompliziertere Objekt-Graphen einlassen. Man muß dabei nur beachten, dass man bevor man ein Objekt freigibt alle Interface-Referenzen auf das Objekt eliminieren muß. Insgesamt habe ich schon viel zu oft so Sätze vom "Objekt und Interfaces nicht mischen" und dem "immer im Hinterkopf behalten von irgendwelchen unsinnigen Regeln" gelesen, dass ich's langsam zum kotzen finde... |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
BTW, wenn Du Deinem Logger-Interface eine Referenz in z.B. einem Singleton-Object verschaffst, wird es sich auch in Wochen nicht selbst zerstören. Wäre halt deftiges Design, statt deftiger Sprüche. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Ihr redet vollkommen aneinander vorbei. Patito bemängelt, dass Interfaces in Delphi anders umgesetzt sind, als sie in anderen Sprachen konzipiert sind - nämlich viel zu COM-orientiert.
Stevie verteidigt hingegen den praktischen Nutzen dieser "Falschumsetzung". Zitat:
Interfaces wären hier tatsächlich um einiges angebrachter, hätte man da nicht die völlig unangebrachte Referenzzählung ins Spiel gebracht. In Delphi heißt "Interface" immer "COM-Objekt". Wenn man dann Interfaces mal so benutzen will, wie in andern Sprachen problemlos möglich, knallts, weil sich die Speichermodelle vermischen. Natürlich kann man sich das ganze jetzt zu Nutzen machen und Frameworks darauf basieren lassen. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Da ich ja auch direkt angesprochen war, will ich auch mal...
Ich habe bisher mit persistenten Objekten gearbeitet, die die gesamten Daten sowie die BL enthielten und beliebig komplex aufeinander verweisen konnten. So konnte ein Personenobjekt ("A.Stahl") von mehreren Vereinsmitglied-, Spieler- und Schiedsrichterobjekten referenziert werden. Öffnet der User ein neues Turnier, werden alle Objekte aufgelöst und neue erzeugt. Über die Owner-Beziehungen wird die Datenstruktur definiert. Nun könnte eine referenzierte Person ("A.Stahl") u.U. irgendwann aufgelöst werden (abgesehen davon, dass das die BL natürlich ggf. auch verhindert). In einem solchen Fall wollte ich gern, dass die betreffenden Personenreferenzen automatisch genilt werden. Meine Überlegung war auch, dass Neulinge der OOP intuitiv davon ausgehen, dass solche Referenzen NIL werden. Das Objekt ist ja halt "weg". Natürlich ist es möglich (das war mir auch damals klar), die Referenzen extra zu verwalten und Referenzen explizit zu nilen. Ich hätte mir jedoch eine Compilerlösung gewünscht, die das automatisch regelt. Soviel zum einfachen Anliegen. Inzwischen habe ich mich mit ein wenig mit Interfaces beschäftigt und will künftig auch mit diesen arbeiten. Dann will ich an zentraler Stelle Interfaces abrufen können (z.B. IPerson(123)), die die Objekte erzeugt und verwaltet und die abgefragten Schnittstellen heraus gibt. Wird ein Objekt länger nicht benötigt, kann es aus einer Liste innerhalb der zentrale freigegeben und damit aufgelöst werden. Ich will also künftig i.d.R. keine festen Verbindungen zwischen den Objekten mehr haben. Statt einer festen Referenz "Player.Person" würde künftig eine Personeninterface anhand der PersonenID abgefordert werden. (So lässt sich auch eine DB vernünftig nutzen und es müssen nicht alle Daten komplett im Hauptspeicher liegen.) Insofern hat sich das Problem für mich eigentlich erledigt (sofern ich das später alles so umsetzen kann, wie eben beschrieben). Bei der klassischen RefCount-Nutzung der Interfaces stört mich eigentlich, dass man noch auf das dahinter liegende Objekt zugreifen kann, obwohl es eigentlich schon aufgelöst sein sollte (weil irgendwo noch eine Referenz "hängen geblieben ist"). Ich könnte also ggf. fleißig weiter auf eine Person zugreifen deren RefCount noch nicht bei 0 liegt, obwohl bereits ein neues Turnier geöffnet wurde). Der Zugriff auf ein aufgelöstest und geniltes Objekt würde wenigstens einen sichtbaren Fehler erzeugen. Fazit: Man sollte Objekte bzw. Interfaces möglichst an einer zentralen Stelle anfordern und verwalten und auf feste (dauerhafte) Referenzen möglichst verzichten. Das scheint mir (nach meiner aktuellen Einschätzung) am sinnvollsten zu sein. (Aber ich lerne als Hobbyprogrammierer halt immer nur stückweise aus meinen eigenen Fehlern. Vielleicht sehe ich das bald schon wieder anders ;-)) EDIT @implementation:Was ist denn der wesentliche Unterschied von Delphi-Interfaces zu Interfaces anderer Sparachen? |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Angenommen du hast ein Handtuch und ein Auto. Was haben die gemeinsam? Man kann sie trocknen. Also implementieren sie beide ITrockenbar. Nun kann es also irgendwo eine Prozedur Trocknen geben, die ein solches ITrockenbar als Parameter will. In Delphi muss man sich dann auch als Aufrufer höllische Gedanken über die Speicherverwaltung machen. Sowas ginge dann schief:
Delphi-Quellcode:
Stattdessen muss man sich dann damit behelfen, komplett nur Interfaces zu nutzen.
var a: TAuto; h: THandtuch;
begin ... try ... Trocknen(a); // Huppsala ... finally ... end; end; Da Objekte und Interfaces aber zur Interaktion konzipiert sind, ist es vollkommener Humbug, beiden verschiedene Speichermodelle zu geben. * Entweder beide manuell freigegeben, oder beide referenzgezählt (->Vala), oder auch für alles GC (->Java,.Net) * der FPC kennt mittlerweile
Delphi-Quellcode:
, wo die Interfaces auf Referenzzählung und sonstige COM-Last verzichten. Könnte Emba auch mal einführen.
{$interfaces corba}
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Hallo,
wenn ich diesen Thread so lese, denke ich der Umgang mit VCL-Objekten ist im C++ Builder in diesem Sinne einfacher geregelt. Da ist z.B. sowas üblich
Code:
Wenn die Funktion verlassen wird, auch über eine Exception, löscht der Smartpointer die Stringlist.
void __fastcall TfrmMain::btnTestClick(TObject *Sender)
{ scoped_ptr<TStringList> list(new TStringList); list->Add(L"Eins"); list->Add(L"Zwei"); list->Add(L"Drei"); } Zitat:
Das Anlegen sähe, wenn der Compiler schon C++11 könnte, dann so aus:
Code:
(Das auto ermittelt den Typ der Variable entsprechend dem Ausdruck auf der rechten Seite der Zuweisung.)
auto liste = make_shared(new TStringList);
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Und wie verwaltet corba dann die Referenzen?
|
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Ein Interface entspräche in etwa nur dem, was man in C++ darunter versteht. Da ist ein Interface nur eine Basisklasse, die nur abstrakte Funktionen enthält. Da es in C++ Mehrfachvererbung gibt, kann eine Klasse so auch beliebig viele Interfaces implementieren. Technisch ist so ein Interface nur eine Tabelle von Funktionszeigern. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Ich frage hier nochmals nach:
Wenn man also Objektreferenzen verwendet, die automatisch genullt werden, wenn das referenzierte Objekt freigegeben wird, wie sieht dann so ein Programm aus? Wie sichert man sich gegen spontan auftretende Nil-Referenzen ab? Kann jemand hier mal ein Beispiel nennen? |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
@Furtbichler:
Ich habe doch oben ein Besipiel beschrieben (wenngleich ich inzwischen ja auch einen anderen Ansatz wählen würde): Eine Person wird von mehreren Spielerobjekten referenziert (damit alle die selben Daten verwenden bzw. anzeigen). Wird die Person freigegeben, sollen alle Spielerobjekte erkennen, dass das Personenobjekt nicht mehr existiert (indem sie das Feld auf nil prüfen). Natürlich sollte vermieden werden, dass das referenzierte Objekt nach dem Prüfen auf nil aufgelöst wird. Aber das ist ja sowiso klar. Natürlich kann man das anders (besser) regeln, aber WENN man rein mit direkt referenzierten Objekten arbeitet, sollte der Wunsch doch nachvollziehbar sein. DataSource informiert ja auch DBGrids, über seine Auflösung (über Observer-Pattern). Im Sinne einer einfachen Programmierung und aus Rücksicht auf OOP-Anfänger wäre es nur ganz schön, wenn es dafür einen Automatismus gäbe. Ich sehe ein, dass das vielleicht nicht ganz einfach zu realisieren ist, aber Deine Nachfrage verstehe ich nicht. MyDBGrid.DataSource wird ja auch nicht spontan auf nil gesetzt, sondern nur wenn die gebundene DataSource aufgelöst wird. @Robotiker: Corba sieht wirklich interessant aus. ![]() ![]() Aber es ist halt wie immer. Ich verstehe das nur zum (Bruch-)Teil. :-( |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Aber Absichern vor spontanen Verschwinden bei Zugriff durch mehrere Threads schreit nach Synchronisation, sollte eigentlich mit einer beliebigen Lösung des Reader-Writer-Problems behandelt werden können. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Zitat:
Zitat:
Ich kann die Motivation von Stahli verstehen, auch der Lösungsansatz bzw. die Idee dahinter von Thom verstehe ich.. Aber notwendig und dann wirklich sauber ist das eben nicht. Ich würde nie im Leben mit einem System arbeiten, das mir die Immanzenz meiner Objekte nicht sicherstellt. Und genau das wird hier gemacht. Ich muss immer und jederzeit damit rechnen, das mir eine Objektreferenz geklaut wird. Grauselig. Aber ich schreibe auch keine heterarchischen Systeme wie Stahli. Vermutlich werde ich ähnliche Gehirnakrobatiken anstellen müssen und dann dankbar diesen Thread lesen, wenn ich das mal mache. Oder ich schreibe keine Heterarchien, stattdessen aber ein Spielermanagement, das mir meine Spieler verwaltet. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
Da ich nicht mit Threads gearbeitet habe und alle Datenobjekte im Speicher verwaltet werden können, funktioniert das letztlich doch zuverlässig. Inzwischen erkenne ich auch, dass es bessere und vor allem flexiblere Verfahrensweisen gibt. Allerdings bringen die natürlich auch einigen Aufwand und Abstraktion mit sich, so dass man sich "im ersten Moment" eher für einen naheliegenderen Ansatz entscheiden wird. Jedenfalls, wenn man mit diesem noch nicht an bestimmte Grenzen gekommen ist und keine Ausbildung als Programmierer genossen hat. Zitat:
Es wäre wohl gut, wenn sich endlich mal ein entsprechendes Konzept durchsetzen würde, mit dem auch Nicht-Profis mit Delphi arbeiten können. Was soll denn jetzt "in" sein? DataSnap? Zitat:
Wie gesagt, ich habe mit meinen Fähigkeiten (ohne Threads) mein Projekt immer weiter ausgebaut und diverse Wege bestritten. Mit den Objekten konnte ich am flexibelsten arbeiten. Die Klassen habe ich mir teilweise durch einen Experten automatisch erzeugen lassen. In einem Framework binde ich die Objekte automatisiert an die GUI. Dabei ist es hilfreich, wenn die Objekte dauerhaft im Speicher vorliegen. Das funktioniert eigentlich wunderbar und mit rel. geringem Aufwand (als das Framework funktionierte). Da ich verschiedene Turniersysteme parallel verwalte und die Spieler/Personen in allen Turniersystemen eingesetzt werden können, wird es inzwischen aber dennoch sehr unübersichtlich. Außerdem möchte ich künftig eine Datenbank oder sogar Client/Server einsetzen. Dann funktioniert es natürlich nicht mehr auf dem bisherigen Weg. Deshalb plane ich eine Änderung des gesamten Konzeptes, muss damit aber natürlich auch erst meine Erfahrungen machen. Man geht halt erst einmal den einfachen Weg, bis man erkennt, dass es so nicht weiter geht... |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
![]() Ein weak_ptr hat z.B. eine Memberfunktion expired() mit der man prüfen kann, ob er noch gültig ist. Greift man auf einen ungültigen Pointer zu gilt Zitat:
Zitat:
Zitat:
![]() Zitat:
Corba ist in der Versenkung verschwunden, die Smartpointer sind heute ISO-Standard. |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Vielen Dank für alle Beiträge! Ich muß zugeben: Jeder hat in etwa das geschrieben, was ich von ihm erwartet habe. :-D
Zuerst muß ich Stevie Recht geben: Diese Probleme entstehen, wenn man eine untypisierte GC Sprache nach Delphi umsetzen möchte... Aber bei diesem Ansatz will ich bleiben und deshalb soll und muß aller Verwaltungsaufwand für den Anwender (des Frameworks) hinter den Kulissen bleiben. Der Programmierer soll sich voll und ganz auf die Umsetzung seiner Ideen konzentrieren können. Deshalb sollen auch keine zusätzlichen Aufrufe notwendig sein. @Furtbichler: Vielen Dank für Deine Antwort! Deine Sorge besteht darin, daß irgendwelche fremden Programmteile Deinem eigenen Code "dazwischen pfuschen". Dieses Gefühl ist bei Multithreadanwendungen vollkommen berechtigt. Leider zeugt das aber auch davon, daß Du Dich - höchstwahrscheinlich - kein einziges mal mit dem von mir angesprochenen Framework beschäftigt hat. Das soll kein Vorwurf sein - schließlich ist das auch keine Pflicht. Hättest Du das aber gemacht, wüßtest Du, das es gar nicht threadsave sein muß, da die JScript-Engine von Microsoft nicht multithreadsicher ist. Spontan auftretende nil-Zeiger der Art
Delphi-Quellcode:
können also gar nicht auftreten. Auch alle asynchronen JScript-Callbacks laufen im Context des Hauptthreads.
if assigned(FMyMarker) then
begin //<- ist hier plötzlich weg FMyMarker. ...; end; Wie threadsicher ein Interface ist, hängt von seiner Implementation ab, nicht von seiner Definition. Wie das INotify.Free umgesetzt wird - ob als direkte, unmittelbare Freigabe in Singlethread-Anwendungen oder "angemeldet" und zeitlich verzögert-, hängt ganz von den Erfordernissen ab. Ich gebe Dir aber vollkommen Recht: In Multithreadanwendungen muß ein höherer Verwaltungsaufwand betrieben werden, als in einem Programm, in dem nur ein Thread (in der Regel der Hauptthread) auf den Datenbestand zugreift. Das vorgestelle Interface wurde von mir primär für das Framework entworfen, um die automatisch Speicherverwaltung von Delphi über Referenzzähler nutzen zu können ohne die Nachteile eines TInterfacedObject's. Für eine darüber hinausgehende Anwendung sind ganz bestimmt noch weitere Aspekte zu berücksichtigen. @himitsu: Das vorgestellte Interface ist von IInterface abgeleitet. Deshalb beinhaltet das implementierende Objekt INotifyObjekt auch eine Referenzzählung mit _AddRef und _Release. Mir geht es aber darum, ein referenzgezähltes Objekt explizit freigeben zu können, ohne daß es zu Zugriffsverletzungen über andere, parallel existierende Referenzen kommt. Die Registrierung/Deregistrierung lokaler (Interface-)Variablen auf dem Stack erfolgt automatisch. Das bedeutet, daß nach Verlassen der Methode die Referenz wieder entfernt wird. Es kann also nicht vorkommen,
Delphi-Quellcode:
daß jetzt, bei Freigabe des Objektes in Button2Click, die (Stack-)Variable "o" auf nil gesetzt wird. "o" wird bei der Zuweisung in Button1Click registriert und nach Beendigung der Methode vom Compiler auf nil gesetzt sowie über INotify.RemoveRef(o) abgemeldet. Alles ganz einfach und vollautomatisch. Ich will das mal "managed Reference" nennen.
type
TForm1 = class([...]) [...] private FMyNotify: INotify; end; procedure TForm1.Button1Click(Sender: TObject); var o: INotify; begin o:=TNotifyObject.Create; FMyNotify:=o; end; procedure TForm1.Button2Click(Sender: TObject); begin FMyNotify:=nil; end; Anders sieht es natürlich bei einer expliziten Registrierung einer Variablen über AddRef() aus - also einer "unmanaged Reference". Hier muß natürlich dafür gesorgt werden, daß es nicht zu Zugriffsverletzungen kommt. Ich würde es einmal vergleichen mit dem Aufruf eines xxx.Lock: Wird anschließend das xxx.Unlock vergessen, gibt es logischerweise Probleme. Das hat aber nichts mit Fehlern im Konzept, sondern mit der Vergeßlichkeit des Programmieres zu tun. Das von mir vorgestelle Interface kann die Arbeit wesentlich erleichtern - ist aber keine Lizenz zum Abschalten des eigenen Gehirns. Meine Umsetztung funktioniert in den bisherigen Tests sehr zuverlässig. @Patito: Hast Du auch irgend etwas Sachliches zu dem Thema beitragen oder beschränkst Du Dich immer auf platte Statements? |
AW: Intelligente Objekte - automatische Freigabe von Referenzen
Zitat:
1. Dass es zumindest fragwürdig ist Dinge auf Design-Fehlern aufzubauen. 2. Dass Interfaces nicht für das Ref-Counting da sind. (Interfaces sind Schnittstellen) Mag sein, dass Dir das nicht gefällt. Insgesamt hat es mit Deiner Situation aber nur insofern zu tun, dass ich Deinen Tricks erst mal wenig vertraue. Ich glaube nicht, dass Du das ganze richtig anpackst. Per Observer ein paar Referenzen zu eleminieren ist zwar gute Technik, aber in einer Anwendung verteilte Referenzen im User-Code lassen sich nicht so einfach alle auf nil setzen. Ich vermute mal, dass Du da in Deinen Tests einem Trick des Optimizers aufgesessen bist...
Delphi-Quellcode:
Wäre interessant wenn Du die Referenzen etwas weiter weg verteilst.
procedure TForm1.Button4Click(Sender: TObject);
var o1, o2: INotifyObject; begin o1:=TNotifyObject.Create; o2:=o1; o1.Free; //sowohl o1 als auch o2 sind jetzt nil!!! end; (Feld in irgendeiner Klasse, auf dem Stack als Rückgabewert einer Funktion, ...). Ich glaube, dass Du niemals alle auf nil gesetzt bekommen wirst. So ein zwischen-Interface bringt reichlich wenig. Anstatt auf Interface-Referenzen aufpassen zu müssen muss man jetzt eben genauso auf die Referenzen auf die INotifyObject-Interfaces aufpassen - damit hat man praktisch nichts gewonnen. Das ganze geht mit Objekt-Handles wesentlich einfacher und ohne 360-Grad Pirouette. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:06 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz