Einzelnen Beitrag anzeigen

Thom

Registriert seit: 19. Mai 2006
570 Beiträge
 
Delphi XE3 Professional
 
#366

AW: Google Maps über COM (Component Object Model)

  Alt 20. Dez 2012, 19:29
Jaaaaa, die Speicherverwaltung. Das ist ein abendfüllendes Thema.

Prinzipiell arbeiten alle Objekte des Frameworks als "Wrapper" oder "Adapter" für bestehende oder neu zu erstellende JavaScript-Objekte. (siehe Grafik). Die JavaScript-Objekte werden über ihre COM-Interfaces angesprochen, die vom Delphi-Wrapper-Objekt gespeichert werden. Solange das Delphi-Objekt also noch existiert, wird auch nicht die Interface-Referenz auf das JavaScript-Objekt freigegeben (Referenz-Zählung) und damit kann auch die JavaScript-Engine des Internet Explorers das (JavaScript-)Objekt nicht freigeben.

Damit möglichst wenig/nichts verloren geht, werden alle vom Framework erstellten Wrapper-Objekte zentral verwaltet und spätestens beim Beenden des Programmes freigegeben.
Unterscheiden muß man dabei die eigentlichen Karten-(Wrapper-)Objekte wie TMap, TRectangle, TMarker usw. und die "Hilfs"-Objekte wie zum Beispiel TMapOptions oder TRectangleOptions. Die Karten-Objekte werden beim Aufruf der entsprechenden Methoden erstellt (Google.Maps.Map, Google.Maps.Rectangle usw.) und anschließend vom Script-Objekt verwaltet (Script.Maps[...], Script.Rectangles[...] usw.).

In JavaScript werden neue Objekte mit der Anweisung "new" erstellt:
Code:
var latLng = new google.maps.latLng(10,20);
[...]
Damit ist auch ersichtlich, daß hier ein neues Objekt angelegt wird. Delphi bietet so etwas nicht, so daß das neue Objekt einfach mit
Delphi-Quellcode:
var LatLng: TLatLng;
  [...]
  LatLng:=Google.Maps.LatLng(10,20);
  [...]
erstellt wird. Viele (Delphi-)Programmierer halten das aber für eine unsaubere Programmierung: Es wird aufgrund des Syntax nicht deutlich, daß an dieser Stelle ein neues Objekt entsteht, wem es "gehört" und wer für dessen Freigabe zuständig ist. Deshalb wurde in der Version 2 des Frameworks die New-Funktion eingeführt, die vom programmtechnischen Standpunkt eigentlich vollkommen überflüssig ist:
Delphi-Quellcode:
function New(Value: Txxx): Txxx;
begin
  Result:=Value;
end;
Sie kann also ohne Probleme weggelassen werden und dient lediglich der besseren Lesbarkeit des Quelltextes:
Delphi-Quellcode:
  Map:=Google.Maps.Map;
  //hat die selbe Funktion wie
  Map:=New(Google.Maps.Map);
Das wird sich in der Version 3 des Frameworks radikal ändern: Die Maps-Methoden liefern dann nur noch eine Objekt-Klasse und erst die New-Funktion erstellt das eigentliche (Wrapper-)Objekt. Damit wird dann die Verwendung der New-Funktion obligatorisch.
Zusätzlich wird es auch für alle "Hilfs"-Objekte (wie TRectangleOptions) Konstruktor-Methoden im entsprechenden Namespace geben:
Delphi-Quellcode:
  RectangleOptions:=New(Google.Maps.RectangleOptions);
  [...]
Die direkte Erstellung über
Delphi-Quellcode:
  RectangleOptions:=TRectangleOptions.Create;
  [...]
wird dann nicht mehr möglich sein, da die entsprechenden Konstruktor-Methoden nicht mehr public sind.

Was auf den ersten Blick wie eine Bevormundung des Programmierers aussieht, hat aber mehrere Gründe. Zum einen zählt dazu die bessere Lesbarkeit und die einheitliche Strukturierung des Quelltextes. Zum anderen gibt es den technischen Aspekt, wenn mehrere Browser-Komponenten verwendet werden: Jedes Objekt muß nämlich im entsprechenden Browser-Kontext erstellt werden und das geht nur, wenn dem neu zu erstellenden Objekt das entsprechende Script-Objekt übergeben wird.
Momentan muß das so geschehen:
Delphi-Quellcode:
  RectangleOptions:=TRectangleOptions.Create(Script(WebBrowser1));
  [...]
Das sieht nicht nur bescheiden aus, sondern ist auch im Gegensatz zum neuen Syntax wesentlich komplizierter:
Delphi-Quellcode:
  RectangleOptions:=New(Google.Maps.RectangleOptions);
  [...]
Da das Objekt Maps "weiß", in welchem Kontext es existiert, kann demzufolge auch das Objekt RectangleOptions korrekt erstellt werden.

Obwohl - wie schon erwähnt - alle Objekte automatisch freigegeben werden (alle Karten-Objekte zum Beispiel auch bei einem Refresh mit der Taste F5) ist es mitunter sinnvoll, sie manuell im Quelltext freizugeben. Das betrifft allerdings in der Regel nur die "Hilfsobjekte" (TxxxOptions).

Trotzdem würde ich Dir empfehlen, momentan keine "Hilfsobjekte" manuell freizugeben, da die neue Frameworkversion nach außen hin ausschließlich mit Interfaces arbeitet und damit eine explizite Freigabe überflüssig wird. (Falls jetzt jemand von den Delphi-Profis kommt und sagt "Interfaces werden nicht freigegeben" - dooooooch, auch das wird gehen. Korrekterweise wird natürlich nicht das Interface freigegeben, sondern das dahinterliegende Objekt.)

Wird allerdings ein Rechteck nicht mehr benötigt, kann es selbstverständlich mit Free in den (digitalen) Mülleimer befördert werden. Es meldet sich dann automatisch bei allen internen Listen ab.
Delphi-Quellcode:
var
  Rectangle: IRectangle;
  RectangleOptions: IRectangleOptions;
begin
  [...]
  RectangleOptions:=New(Google.Maps.RectangleOptions);
  RectangleOptions.xxx;
  [...]
  Rectangle:=New(Google.Maps.Rectangle(RectangleOptions));
  [...]
end; //<- hier wird RectangleOptions automatisch freigegeben, Rectangle bleibt selbstverständlich erhalten
Das Speichermanagement-Problem wird sich also mit der Umstellung auf die kommende Version quasi von selbst erledigen. Um darauf gerüstet zu sein, sollte man keine harten Typumwandlungen mehr vornehmen, sondern jetzt schon den as-Operator verwenden:
Delphi-Quellcode:
  //schlecht:
  TRectangle(Sender).xxx;
  //besser:
  (Sender as TRectangle).xxx;
Damit ist dann die Umstellung schnell erledigt:
Delphi-Quellcode:
  //geht nicht:
  IRectangle(Sender).xxx;
  //geht:
  (Sender as IRectangle).xxx;
Thomas Nitzschke
Google Maps mit Delphi

Geändert von Thom (20. Dez 2012 um 23:27 Uhr)
  Mit Zitat antworten Zitat