Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Objekt freigeben (https://www.delphipraxis.net/115962-objekt-freigeben.html)

Yheeky 20. Jun 2008 15:52


Objekt freigeben
 
Hi,

ich habe eben ziemlich lange über einem Problem gehangen, aber es inzwischen "gelöst". Folgende Sache tritt bei meinem Projekt auf:
Ich habe ein eigenes Objekt von TObject abgeleitet (TDaten).

Ich habe nun eine Funktion, die ein solches Objekt erstellt und Daten ins Internet sendet. Abschließend wollte ich das Objekt wieder freigeben, aber genau da kam ein Fehler.

Delphi-Quellcode:
var tempAuto : TAuto;
begin
tempAuto := TAuto.Create;
tempAuto := autoSammlung.GetAutoByName('Opel');

idHTTP.Get('...');

tempAuto.Free; // <-- hier kommt eine Zugriffsverletzung. Wenn ich die Zeile rauslasse, funktioniert alles wie gewollt
end;
Auch die Variante mit try...finally hat nicht funktioniert:

Delphi-Quellcode:
var tempAuto : TAuto;
begin
tempAuto := TAuto.Create;
tempAuto := autoSammlung.GetAutoByName('Opel');

try
  idHTTP.Get('...');
finally
  tempAuto.Free; // <-- auch hier der Fehler
end;

end;
Kann mir jemand erklären, woran die Zugriffsverletzung liegt?

RavenIV 20. Jun 2008 16:07

Re: Objekt freigeben
 
Ein Wenig mehr Information bitte.

Wie sieht autoSammlung.GetAutoByName('Opel'); aus?
Und was passiert bei idHTTP.Get('...'); ?

Hinweise:
Vermutlich kannst / musst Du Dir das "Create" und das "Free" sparen.
Die Objekte existieren schon autoSammlung.
tempAuto ist dann nur ein Verweis auf ein Element von autoSammlung.

Yheeky 20. Jun 2008 16:20

Re: Objekt freigeben
 
Danke für den kleinen versteckten Hinweis ;-) Habe den Fehler gerade gefunden. Es liegt tatsächlich in der Funktion GetAutoByName, obwohl mit hier beim Debuggen kein Fehler angezeigt wird!

Erklärung anhand eines Ausschnittes der Unit:

Delphi-Quellcode:
function TAuto.getAuto(Index: Cardinal): TAuto;
begin
  Result := TAuto(inherited Items[Index]);
end;

function TAuto.GetAutoByName(Autoname: String): TAuto:
var
  I: Integer;
  tempAuto : TAuto;
begin
  result := nil;

  if Count <> -1 then
  begin
    for I := 0 to Count - 1 do
    begin
      tempAuto := TAuto(getItem(I)); // <-- hier liegt der Fehler

      if tempAuto.Name = Autoname then
      begin
        result := tempAuto;
        Exit;
      end;
    end;
  end;
end;

Anstatt getItem, welches in der Basisklasse verfügbar ist, muss ich natürlich getAuto aufrufen. Dann funktioniert es auch! Danke nochmal für den Denkanstoss! Sehr ihr, manchmal muss man garnicht viel helfen ;-)

MacGuyver 20. Jun 2008 16:26

Re: Objekt freigeben
 
Tach,

das Typecasting über
Delphi-Quellcode:
TAuto(getItem(I))
ist gefährlich. Besser wäre
Delphi-Quellcode:
:=getItem(I) as TAuto
weil es zur Exception kommt, wenn der Typ nicht stimmt.

Stefan

Yheeky 20. Jun 2008 17:14

Re: Objekt freigeben
 
Das sollte zwar relativ sicher sein, aber ist noch eine gute Ergänzung, danke!

jottkaerr 22. Jun 2008 12:25

Re: Objekt freigeben
 
Auch wenn Du inzwischen die Methode GetAutoByName() als Schuldigen ausgemacht und korrigiert hast, stecken im folgenden Code noch zwei Fehler.

Zitat:

Zitat von Yheeky
Delphi-Quellcode:
var tempAuto : TAuto;
begin
tempAuto := TAuto.Create;
tempAuto := autoSammlung.GetAutoByName('Opel');

try
  idHTTP.Get('...');
finally
  tempAuto.Free;
end;

end;

Zum einen erzeugst Du ein TAuto, aber die einzige Referenz auf dieses Objekt überschreibst Du in der nächsten Zeile sofort wieder. Somit hast Du ein Speicherleck.

Der zweite Fehler ist noch viel schwerwiegender und kann im weiteren Verlauf zu einer Zugriffsverletzung oder Ähnlichem führen. Über GetAutoByName() lässt Du dir eine Referenz auf ein Objekt geben, das Du später freigibst, ohne dass die autoSammlung etwas davon erfährt (ich gehe mal davon aus, dass sich das Objekt im Destruktor nicht bei autoSammlung abmeldet). Bei einem späteren Aufruf von GetAutoByName() kann es passieren, dass versucht wird, auf dieses nicht mehr existierende Objekt zuzugreifen (z.B. für den Namensvergleich). Und wenn autoSammlung sich auch um das Freigeben der von ihr verwalteten Objekte kümmert, wird sie irgendwann später den Destruktor ein zweites Mal aufrufen -- und dadurch in nicht mehr alloziertem Speicher wildern.

Und selbst wenn sich das TAuto-Objekt im Destruktor sauber bei autoSammlung abmeldet: Du wirst nach dem Ausführen des obigen Codes keinen Opel mehr in der Sammlung finden. Das dürfte wahrscheinlich nicht gewünscht sein.

Beheben kannst Du beide Probleme, indem Du entweder eine methode TAuto.Assign einführst, die die Propertys eines anderen TAuto-Objekts übernehmen kann:

Delphi-Quellcode:
var tempAuto : TAuto;
begin
  tempAuto := TAuto.Create;

  try
    tempAuto.Assign(autoSammlung.GetAutoByName('Opel'));
    idHTTP.Get('...');
  finally
    tempAuto.Free; // <-- auch hier der Fehler
  end;

end;
Oder Du verzichtest auf die Freigabe, denn autoSammlung kümmert sich (hoffentlich) selbst darum:

Delphi-Quellcode:
var tempAuto : TAuto;
begin
  tempAuto := autoSammlung.GetAutoByName('Opel');

  idHTTP.Get('...');
end;

RavenIV 23. Jun 2008 08:00

Re: Objekt freigeben
 
Zitat:

Zitat von jottkaerr
Auch wenn Du inzwischen die Methode GetAutoByName() als Schuldigen ausgemacht und korrigiert hast, stecken im folgenden Code noch zwei Fehler.

Genau das wollte ich doch mit meinem Beitrag erreichen.
Aber ich wollte ihm halt nicht die komplette Lösung auf den Teller legen, sondern nur in die richtige Richtung schubsen.
Anscheînend war der Schubser nicht stark genug. ;-)

Yheeky 23. Jun 2008 14:43

Re: Objekt freigeben
 
@RavenIV: Nein, leider war der Schubser nicht stark genug ;-)

Danke, für die Information. Ich weiss leider noch nicht so recht Bescheid über Objekte und konnte eueren Code auch leider noch nicht ganz nachvollziehen. Hatte mir mal ein Tutorial über Objekte und Objektlisten angeschaut, aber scheinbar habe ich noch lücken. Könnt ihr mir vielleicht nochmal einen kurzen Crash-Kurs geben? Wie ist das mit Referenzen, Speicherleck, wann muss ich Objekte mit Create initialisieren, wann darf ich sie mit Free freigeben, etc...? Wäre super!

rollstuhlfahrer 23. Jun 2008 16:06

Re: Objekt freigeben
 
Objekte musst du mit Create initialisteren, wenn du dann diese Instanz verwenden willst. Willst du eine andere Instanz verwenden, wird diese einfach der anderen zugewiesen. Da sich Objekte wie Pointer verhalten, gibts dabei keine Probleme. Free brauchst du dann, wenn du das Objekt nicht mehr verwenden willst. Dies ist tödlich bei Objekten, die du von anderen Instanzen geholt hast, da dann auch die andere Instanz gelöscht wird.

Tipp nebenbei: Falls Free fehlschlägt, gibts ein Speicherleck. Nutze doch FreeAndNil().

Bernhard

EDIT: Eigentlich gilt: Neue Frage, neues Thema

SirThornberry 23. Jun 2008 16:25

Re: Objekt freigeben
 
Zitat:

Zitat von rollstuhlfahrer
EDIT: Eigentlich gilt: Neue Frage, neues Thema

:thumb:
@Fragesteller: mache für deine neue Frage bitte ein neues Thema auf. Das hat den Vorteil das du dem Thema entsprechend dem neuen Inhalt dann auch einen aussagekräftigen Titel geben kannst. Und das hat dann gleich wieder den Vorteil das man es über die Suche schneller findet wenn man das gleiche Problem hat.

Yheeky 23. Jun 2008 18:29

Re: Objekt freigeben
 
Okay, mach' ich :-)

@rollstuhlfahrer: Danke schonmal für die Antwort :-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:01 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