|
Registriert seit: 19. Mai 2006 570 Beiträge Delphi XE3 Professional |
#1
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. |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |