![]() |
Korrekte Objekt - Freigabe?
Meine Zweifel an meiner Lösung für die Abfrage von Playlist - Daten von Spotify über die Web-API
![]() 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:
Alle Elemente des Arrays 'playlist.tracks.items' werden bei der Freigabe der TPlaylist - Instanz liquidiert, so dass keine Memory - Leaks übrigbleiben.
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; 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 |
AW: Korrekte Objekt - Freigabe?
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. |
AW: Korrekte Objekt - Freigabe?
Zitat:
Delphi-Quellcode:
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)
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; Gruß LP |
AW: Korrekte Objekt - Freigabe?
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? |
AW: Korrekte Objekt - Freigabe?
Zitat:
Gruß LP |
AW: Korrekte Objekt - Freigabe?
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 |
AW: Korrekte Objekt - Freigabe?
Zitat:
Gruß LP |
AW: Korrekte Objekt - Freigabe?
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. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:36 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz