AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Korrekte Objekt - Freigabe?
Thema durchsuchen
Ansicht
Themen-Optionen

Korrekte Objekt - Freigabe?

Ein Thema von Maekkelrajter · begonnen am 5. Dez 2020 · letzter Beitrag vom 6. Dez 2020
Antwort Antwort
Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
156 Beiträge
 
Delphi 12 Athens
 
#1

Korrekte Objekt - Freigabe?

  Alt 5. Dez 2020, 16: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
Aviator

Registriert seit: 3. Jun 2010
1.611 Beiträge
 
Delphi 10.3 Rio
 
#2

AW: Korrekte Objekt - Freigabe?

  Alt 5. Dez 2020, 21:44
Wenn ich deinen Code jetzt richtig deute - mir fehlen da ggf. noch ein paar Informationen zu den verwendeten Klasse - dann ist es so, dass du beim Hinzufügen der Elemente aus dem Array zur TObjectList<T> die TObjectList<T> zum Besitzer der Elemente machst. Durch das Hinzufügen wird nur ein Pointer auf das Objekt in die ObjectList eingefügt die zuvor auch im TArray verwendet wurde. Wenn die ObjectList mit OwnsObjects = True erzeugt wurde, was dem Standard entspricht, dann werden die Objekte auch entsprechend freigegeben.

Du kannst ja mal im Debugger die Pointer auf die jeweiligen Objekte vergleichen. Diese müssten identisch sein.
  Mit Zitat antworten Zitat
Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
156 Beiträge
 
Delphi 12 Athens
 
#3

AW: Korrekte Objekt - Freigabe?

  Alt 5. Dez 2020, 23:10
Wenn ich deinen Code jetzt richtig deute - mir fehlen da ggf. noch ein paar Informationen zu den verwendeten Klasse - dann ist es so, dass du beim Hinzufügen der Elemente aus dem Array zur TObjectList<T> die TObjectList<T> zum Besitzer der Elemente machst. Durch das Hinzufügen wird nur ein Pointer auf das Objekt in die ObjectList eingefügt die zuvor auch im TArray verwendet wurde. Wenn die ObjectList mit OwnsObjects = True erzeugt wurde, was dem Standard entspricht, dann werden die Objekte auch entsprechend freigegeben.
Die Elemente werden nicht einer ObjectList, sondern ebenfalls einem Array hinzugefügt:
Delphi-Quellcode:
  TPaging<TItem: class> = class
  private
    FItems: TArray<TItem>; // enthält die Track-Objekte; hierhin wird kopiert
    FOffset: Integer;
    FTotal: Integer;
    FLimit: Integer;
    FHref: string;
    FNext: string;
    FPrevious: string;
    function ExpandItems(NewLength: Integer):Integer;
    function AddItem(Item: TItem; var Position: Integer): Boolean;
    Function GetTrackcount: Integer;
  public
    destructor Destroy; override;
    property Href: string read FHref;
    property Items: TArray<TItem> read FItems;
    property Limit: Integer read FLimit;
    property Next: string read FNext;
    property Offset: Integer read FOffset;
    property Previous: string read FPrevious;
    property Total: Integer read FTotal;
    property count: Integer read GetTrackCount;
  end;

  TPlayList = class
  private
    FName: string;
    FTracks: TPaging<TPlaylistTrack>;
  public
    destructor Destroy; override;
    property Name: string read FName;
    property Tracks: TPaging<TPlaylistTrack> read FTracks;
  end;
Bei ersten Versuchen hatte ich beim Freigeben von 'nextpage' auch die Elemente des Arrays entsorgt. Das hatte prompt zur Folge, dass die kopierten Zeiger im Playlist-Objekt ins Nirwana zeigten und Accessviolations erzeugten. In der aktuellen Version werden alle Items, auch die hinzugefügten, erst zusammen mit dem Playlist-Objekt freigegeben. Jedenfalls bleiben keine 'Speicherleichen' zurück (ReportMemoryLeaksOnShutdown = true)

Gruß LP
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.611 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: Korrekte Objekt - Freigabe?

  Alt 5. Dez 2020, 23:23
Naja gut. Das es ein Array und kein TObjectList ist, war aus deinem Code nicht ersichtlich. Zumindest nicht für mich bei schnellem Drüberlesen.

Aber im Endeffekt verhält es sich genau gleich. Auch dort werden nur die Instanzen "weitergegeben" - sprich der Pointer gespeichert - das Objekt wird aber nicht kopiert. In dem Fall gibt die ObjectList die Instanzen nicht frei sondern du selbst beim Durchlaufen des Arrays.

Hast du dir im Debugger die Adressen der einzelnen Objekte mal angeschaut?
  Mit Zitat antworten Zitat
Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
156 Beiträge
 
Delphi 12 Athens
 
#5

AW: Korrekte Objekt - Freigabe?

  Alt 6. Dez 2020, 10:39
Hast du dir im Debugger die Adressen der einzelnen Objekte mal angeschaut?
Sie sind - erwartungsgemäß - identisch. Wie sollte es auch anders sein, wenn die Objekte, sprich die Zeiger, kopiert wurden. Meine Frage war ja, ob die durch diese Zeiger referenzierten Objekte nach der Freigabe von 'NextPage' quasi 'autark' weiterleben oder irgendwie als 'obsolet' gekennzeichnet werden.

Gruß LP
  Mit Zitat antworten Zitat
TurboMagic

Registriert seit: 28. Feb 2016
Ort: Nordost Baden-Württemberg
2.938 Beiträge
 
Delphi 12 Athens
 
#6

AW: Korrekte Objekt - Freigabe?

  Alt 6. Dez 2020, 10:51
So wie ich dich/das verstanden haben:
wenn du zwei Variablen hast, die auf den selben Speicher zeigen und du
eine davon freigibst ist der SPeicher wirklich freigegeben und darf auch
nicht mehr über die andere Variable benutzt werden!

Ausnahmen:

1. Interface referenzen, hier wird erst freigegeben wenn der Referenzzähler
aller Referenzen auf 0 geht (weil die entsprechende Referenz eine lokale Variable
war und die entsprechende Methode verlassen wurde oder weil jemand die Referenz auf
NIL gesetzt hat).

2. Mobile Plattformen vor 10.4. Die nutzten ARC was in etwa so funktioniert wie
1. mit den Interfaces
  Mit Zitat antworten Zitat
Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
156 Beiträge
 
Delphi 12 Athens
 
#7

AW: Korrekte Objekt - Freigabe?

  Alt 6. Dez 2020, 11:18
wenn du zwei Variablen hast, die auf den selben Speicher zeigen und du
eine davon freigibst ist der SPeicher wirklich freigegeben und darf auch
nicht mehr über die andere Variable benutzt werden!
Wenn das Objekt ein Array of Object enthält, wird das doch eben nicht automatisch mit freigegeben, sondern muss explizit Element für Element freigegeben werden. Das jedoch geschieht bei der Freigabe von 'NextPage' genau nicht!

Gruß LP

Geändert von Maekkelrajter ( 6. Dez 2020 um 11:21 Uhr) Grund: Ergänzung
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.611 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: Korrekte Objekt - Freigabe?

  Alt 6. Dez 2020, 12:22
Nochmal: Du überträgst sozusagen den Besitz der Objektinstanzen auf ein anderes Array. Dessen Elemente gibst du beim Freigeben der übergeordneten Klasse auch frei.

Da die Objektinstanzen identisch sind, wie du ja an den Adressen beim Debuggen gesehen hast, kannst du diese auch nur einmal freigeben. Wenn du sie mehrfach freigibst, kommt es logischerweise im späteren Verlauf zu Zugriffsverletzungen wie du ja ebenfalls festgestellt hast.

Anders wäre es, wenn du eine Kopie des Objektes erstellen und in dem zweiten Array speichern würdest. Dann müsstest du auch die Instanzen des ersten Arrays freigeben.

Geändert von Aviator ( 6. Dez 2020 um 12:25 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:28 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz