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;