Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Peinliche Frage zu generischen Listen (https://www.delphipraxis.net/191006-peinliche-frage-zu-generischen-listen.html)

Sherlock 29. Nov 2016 10:14

Peinliche Frage zu generischen Listen
 
Ich sehe gerade den Wald vor lauter Bäumen nicht, und komme nicht mehr voran. Ich schildere mal was ich erreichen wollte, und dann zeige ich was ich getan habe, und was damit passiert :(

Ich habe viele Messwerte (es geht im Prinzip immer noch um das schnelle Zeichnen). Die wollte ich gerne in einer Liste ablegen, damit ich einfach neue Werte hinzufügen und genauso einfachen Zugriff habe. Dazu habe ich mir einen Typ für ein Listenelement deklariert und dann zunächst eine simple generische Liste basierend auf diesem Typ. Es zeigte sich dann, daß diese Liste zu einfach ist. Mir fehlte insbesondere eine etwas ausgefeiltere Add-Methode, die nur neue Elemente hinzufügt, d.h. prüft ob das einzufügende Element bereits in der Liste vorhanden ist (Erkennung über einen Timestamp). Also habe ich eine neue Klasse definiert, die die generische Liste beinhaltet und meine Implementierungen von Add, Count und einem Get drangegflanscht. Mein Problem ist jetzt, daß ich Objekte dieser Klasse offenbar nicht komplett aus dem Speicher bekomme.

Hier das wesentliche zu den Klassen:
Delphi-Quellcode:
  // Es gibt noch einen Stapel weitere Werte in dieser Klasse, die allesamt single sind,
  // damit tragen sie nicht mehr zum Problem bei, als die hier noch aufgeführten Werte
  TDevValue = class
  private
    fTimeOffset: single;
    fQUALITY1: string;
    fQUALITY2: string;
    fQUALITY3: string;
    fMQUALITY: string;
    fTime   : TDateTime;
    fMOVEMENT: Boolean;
    procedure Init;
  public
    property TimeOffset: single read fTimeOffset;
    property QUALITY1: string read fQUALITY1;
    property QUALITY2: string read fQUALITY2;
    property QUALITY3: string read fQUALITY3;
    property MQUALITY: string read fMQUALITY;
    property Time   : TDateTime read fTime;
    property MOVEMENT: Boolean read fMOVEMENT;
    constructor Create(aRow: string; aFS: TFormatSettings);
    destructor Destroy; override;
  end;

  TValueList = class
  private
    fLastTimeStamp: single;
    fDevValueList : TList<TDevValue>;
    fs           : TFormatSettings;
    function GetItemCount: Integer;
  public
    function Add(aRow: string): Integer;
    function Get(aIndex: Integer): TDevValue;
    property Count: Integer read GetItemCount;
    constructor Create;
    destructor Destroy; override;
  end;
Hier die Implementierung. Kleiner Hinweis zum Konstruktor eines TDevValue: Es wird ein String erwartet, der eine Zeile einer csv-Datei repräsentiert.

Delphi-Quellcode:
constructor TDevValue.Create(aRow: string; aFS: TFormatSettings);
var
  myRow: TStringList;
begin
  inherited Create;
  Self.Init;
  if aRow.Length > 20 then
  begin
    myRow := TStringList.Create;
    try
      myRow.Delimiter := ';';
      myRow.StrictDelimiter := True;
      myRow.DelimitedText := aRow;

      fTimeOffset := StrToFloatDef(myRow[keyTimeOffset], 0, aFS);
      fQUALITY1 := myRow[keyQUALITY1];
      fQUALITY2 := myRow[keyQUALITY2];
      fQUALITY3 := myRow[keyQUALITY3];
      fMQUALITY := myRow[keyMQUALITY];
      fTime := UnixToDateTime(Floor(StrToFloatDef(myRow[keyTime], 0, aFS)), False);
      fMOVEMENT := myRow[keyMOVEMENT] = '1';
    finally
      myRow.Free;
    end;
  end;
end;

destructor TDevValue.Destroy;
begin
  // Der vermutlich klägliche Versuch UniCodeString Memoryleaks zu beseitigen
  fQUALITY1 := '';
  fQUALITY2 := '';
  fQUALITY3 := '';
  fMQUALITY := '';
  inherited;
end;

procedure TDevValue.Init;
begin
  fTimeOffset := -1;
  fQUALITY1 := '';
  fQUALITY2 := '';
  fQUALITY3 := '';
  fMQUALITY := '';
  fTime := NaN;
  fMOVEMENT := False;
end;

/////// Jetzt die Liste //////


function TValueList.Add(aRow: string): Integer;
var
  found    : Boolean;
  i        : Integer;
  myDevValue: TDevValue;
begin
  found := False;
  i := 0;
  myDevValue := TDevValue.Create(aRow, fs);
  if fDevValueList.Count > 0 then
  begin
    repeat
      if myDevValue.TimeOffset = fDevValueList[i].TimeOffset then
        found := True
      else
        Inc(i);
    until found or (i > fDevValueList.Count - 1);
  end;
  if not found then
  begin
    Result := fDevValueList.Add(myDevValue);
    if myDevValue.TimeOffset > fLastTimeStamp then
      fLastTimeStamp := myDevValue.TimeOffset;
  end
  else
  begin // Was ich nich brauche, kann gleich wieder weg
    myDevValue.Free;
    Result := i;
  end;
end;

constructor TValueList.Create;
begin
  fDevValueList := TList<TDevValue>.Create;
  // Die csv-Datei liefert numerische Werte mit Punkt als Dezimaltrenner
  fs := TFormatSettings.Create;
  fs.DecimalSeparator := '.';
  fLastTimeStamp := 0;
end;

destructor TValueList.Destroy;
begin
  // Was ist hier zu tun, um wirklich die einzelnen Listenelemente freizugeben?
  fDevValueList.DeleteRange(0, fDevValueList.Count - 1);
  fDevValueList.Free;
  inherited;
end;

function TValueList.Get(aIndex: Integer): TDevValue;
begin
  if (aIndex >= 0) and (aIndex < fDevValueList.Count) then
    Result := fDevValueList[aIndex]
  else
    Result := nil;
end;

function TValueList.GetItemCount: Integer;
begin
  Result := fDevValueList.Count;
end;
Irgendwie ist das alles trivial einfach...aber FastMM schimpft ganz dolle mit mir, daß ich praktisch keines der vielen tausend TDevValue Objekte freigegeben habe. Was habe ich falsch gemacht?


Sherlock

Gruber_Hans_12345 29. Nov 2016 10:19

AW: Peinliche Frage zu generischen Listen
 
kenn mich mit genereischen listen nicht aus (hab die noch nicht)aber musst du da die Clear procedure der TList nicht überschreiben und die Dinger dann Freen die in der Liste sind?

Lemmy 29. Nov 2016 10:23

AW: Peinliche Frage zu generischen Listen
 
bei Fragen die dir peinlich sind, hilft es ab und zu in die Hilfe zu schauen:
http://docwiki.embarcadero.com/Libra...lections.TList

Zitat:

Die Klasse TObjectList stammt von TList ab und stellt einen automatischen Mechanismus für die Freigabe von Objekten bereit, die aus der Liste entfernt wurden.
sprich: TList besitzt keinen solchen Mechanismus und damit muss mal selber aufräumen oder TObjectlist<> verwenden

haentschman 29. Nov 2016 10:25

AW: Peinliche Frage zu generischen Listen
 
Moin... :P

Ohne alles gelesen zu haben... :P
TList<> nach TObjectList<> ... schaust zu hier: http://docwiki.embarcadero.com/Libra...ectList.Create
OwnsObjects = True

Ritsch 29. Nov 2016 10:26

AW: Peinliche Frage zu generischen Listen
 
Ich glaube bei TList musst du dich selbst darum kümmern die Objekte in der Liste freizugeben.
Also beim Destory alle Items durchgehen und diese freigeben.

Mit TObjectList kannst du die Eigenschaft "OwnsObject" setzen. Damit kümmert sich die Liste selbst darum die Items freizugeben beim Aufruf von .Free.

Zu langsam :D

Sherlock 29. Nov 2016 10:35

AW: Peinliche Frage zu generischen Listen
 
Menno, keine rote Box. So einfach geht das...

Mache mich gleich dran. Danke!

Sherlock

Aviator 29. Nov 2016 10:47

AW: Peinliche Frage zu generischen Listen
 
Es wurde ja schon mehrfach erwähnt. Die TList Klasse gibt die Elemente die sie beinhaltet nicht wieder automatisch frei. Siehe auch TList.Destroy (die Anmerkung). Deshalb wäre es vielleicht sinnvoll, auf eine TObjectList umzusteigen und diese mit OwnsObjects = True zu erzeugen.

Alternativ müsstest du im Destructor deiner TValueList Klasse durch alle Elemente iterieren und diese dann freigeben.

Übrigens:

Delphi-Quellcode:
  if fDevValueList.Count > 0 then
  begin
    repeat
      if myDevValue.TimeOffset = fDevValueList[i].TimeOffset then
        found := True
      else
        Inc(i);
    until found or (i > fDevValueList.Count - 1);
  end;
Kann man auch so schreiben:

Delphi-Quellcode:
  if fDevValueList.Count > 0 then
  begin
    repeat
      if myDevValue.TimeOffset = fDevValueList[i].TimeOffset then
        found := True
      else
        Inc(i);
    until found or (i = fDevValueList.Count); // <--------  ÄNDERUNG HIER
  end;
Mir stellt sich jetzt nur noch die Frage, wieso du überhaupt noch die Liste durchlaufen musst. Du setzt dir doch schon in
Delphi-Quellcode:
fLastTimeStamp
den zuletzt hinzugefügten TimeStamp. Reicht es dann nicht einfach zu überprüfen, ob der neue TimeStamp größer/neuer ist als der zuletzt hinzugefügte?

Sherlock 29. Nov 2016 10:48

AW: Peinliche Frage zu generischen Listen
 
Ja, schon sind die Objekte alle freigegeben worden, und ich kann mich dem Freigeben der Bitmaps widmen.... Danke Euch allen!
Und Aviator, danke für die Optimierung...ich stand wohl sehr weit neben mir. Im Nachbarort, sozusagen :D

Sherlock

Aviator 29. Nov 2016 11:05

AW: Peinliche Frage zu generischen Listen
 
Zitat:

Zitat von Sherlock (Beitrag 1354908)
Und Aviator, danke für die Optimierung...ich stand wohl sehr weit neben mir. Im Nachbarort, sozusagen :D

Keine Ursache. :-D

Hast du denn noch meine zusätzliche Anmerkung gelesen? Kannst du dich auf den TimeStamp verlassen den du dir zwischenspeicherst?

himitsu 29. Nov 2016 11:14

AW: Peinliche Frage zu generischen Listen
 
Das IF könnte man och noch wegoptimieren, wenn man die Schleife zuerst prüft.
Delphi-Quellcode:
    while i < fDevValueList.Count do begin
      //if myDevValue.TimeOffset = fDevValueList[i].TimeOffset then begin
      if SameValue(myDevValue.TimeOffset, fDevValueList[i].TimeOffset) then begin
        found := True;
        Break;
      end;
      Inc(i);
    end;
Aber wichtiger wären die Float-Vergleiche, denn die Floats haben ja dieses winzige Rundungsproblemchen.
Delphi-Referenz durchsuchenSameValue


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:41 Uhr.
Seite 1 von 2  1 2   

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