Meine Zweifel an meiner Lösung für die Abfrage von Playlist - Daten von Spotify über die Web-
API (Siehe hier, Artikel #14) haben mich veranlasst, die Routine noch einmal zu überarbeiten. Vor allem die Schleife, in der für jedes Element des Objekt-Arrays ein Aufruf von TJson.JsonToObject erfolgte, störte mich. Dasselbe gilt für die schrittweise Erweiterung des Arrays 'playlist.tracks.items', wobei ja bekanntlich jedesmal neuer Speicher für das vergrößerte Array alloziert wird und die schon vorhandenen Elemente dorthin kopiert werden.
Das Array 'playlist.tracks.items' wird jetzt mit einer einzigen Aktion auf die erforderliche Größe gebracht, die beim Aufruf der ersten Seite in 'playlist.tracks.total' von der Spotify Web-
API geliefert wird.
Die jeweils neue Seite des TPaging - Objektes wird nun mit TJson.JsonToObject komplett in das lokale Objekt 'nextpage' überführt, von dem aus die ObJekte des Arrays in einer Schleife nach 'playlist.tracks' kopiert werden. Anschließend wird 'nextpage' freigegeben, wobei (in meiner Implementierung) die Elemente (Objekte) des Arrays nicht berücksichtigt werden, die ja weiterhin in 'playlist.tracks.items' gültig sein sollen sollen.
Delphi-Quellcode:
TNextPlPage = Class
private
FItems: TArray<TPlaylistTrack>;
public
property Tracks: TArray<TPlaylistTrack> read FItems;
End;
function TSpManager.GetPlaylistTracks(var Playlist: TPlaylist; PlaylistID: string): Integer;
var JValue: TJSONValue;
Track: TPlaylistTrack;
limit,offset,totalItems,pageItems: Integer;
nextpage: TNextPlPage;
const DefaultLimit = 100;
begin
result := 0;
offset := 0;
limit := DefaultLimit;
JValue:= SendRequest('v1/playlists/' + PlaylistID,0,limit);
if JValue is TJSONObject then
begin
playlist := TJson.JsonToObject<TPlaylist>(TJSONObject(JValue));
If (playlist = NIL) Then exit;
totalItems := playlist.Tracks.total;
Limit := playlist.Tracks.limit;
If totalitems > limit Then
begin
pageItems := 0;
offset := playlist.tracks.ExpandItems(totalitems); // Playlist.tracks wird auf volle Länge erweitert; offset = bisherige Länge als Rückgabewert
repeat
Jvalue := SendRequest('v1/playlists/' + PlaylistID + '/tracks',offset,Limit); // nächste 'Seite' holen
Inc(pageItems,limit);
If JValue is TJSONObject Then
begin
Nextpage := TJson.JsonToObject<TNextPlPage>(TJSONObject(JValue));
If nextpage <> NIL Then
begin
For Track in Nextpage.Tracks do playlist.tracks.AddItem(Track,offset); // fügt 'Track' - Objekt hinzu und inkrementiert 'offset'
FreeAndNIL(nextpage); // gibt 'nextpage' frei, nicht aber die Objekte des Arrays, die jetzt zu 'Playlist' gehören sollen
end else continue;
end;
until pageItems >= totalItems;
If offset < totalitems Then
begin
messageDlg('Es konnten nicht alle Daten übertragen oder gefunden werden', mtError,[mbOK],0);
playlist.tracks.ExpandItems(offset); // leere Items 'abschneiden'
end;
end;
result:= Playlist.tracks.count;
end;
end;
Alle Elemente des Arrays 'playlist.tracks.items' werden bei der Freigabe der TPlaylist - Instanz liquidiert, so dass keine Memory - Leaks übrigbleiben.
Jetzt meine Frage: Ist das Zufall, dass diese Lösung wie gewünscht funktioniert? Sind die Objekte von 'nextpage' nach der Freigabe nicht quasi 'herrenlos' und könnten irgendwann mit anderem Inhalt überschrieben werden?
Gruß LP