Einzelnen Beitrag anzeigen

Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
118 Beiträge
 
Delphi 11 Alexandria
 
#1

Korrekte Objekt - Freigabe?

  Alt 5. Dez 2020, 15:30
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
  Mit Zitat antworten Zitat