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 VirtualStringTree: Speicherleck (https://www.delphipraxis.net/142392-virtualstringtree-speicherleck.html)

guidok 27. Okt 2009 13:01


VirtualStringTree: Speicherleck
 
Hallo zusammen!

Ich habe mir gerade mal meine werdende Anwendung mit FastMM angeschaut und auch prompt Speicherlecks gemeldet bekommen.

Ich konnte mittlerweile auch einen VST als "Täter" identifizieren. Ich füge da mehrere Knoten mit Unterknoten mit Hilfe von "AddChild" von folgender Struktur ein:

Delphi-Quellcode:
  PNodeData = ^TNodeData;
  TNodeData = record
    Id: Integer;      //Identifikation des Feldes
    AuthReq: Integer; //Benötigte Authorisierung;
    Text: String;     //Beschriftung
  end;
Im Ereignis OnFreeNode des VST mache ich folgendes:

Delphi-Quellcode:
procedure TSettingsForm.vstFreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
  var
    NodeData: PNodeData;
begin
  NodeData := vst.GetNodeData(Node);
  if Assigned(NodeData) then begin
    //NodeData^.Text := '';
    Finalize(NodeData^.Text);
  end;
end;
Sooooo. Die Speicherlecks werden damit schon einmal weniger, aber es sind durchaus noch welche vorhanden. Sobald ich alle Knoten des Trees einmal aufgeklappt habe, sind sie dann alle weg.

Ist das ein Fehler vom VST oder mache ich noch etwas falsch?


Edit:

Ich habe mir gerade überlegt, ob es etwas mit den "Initialisieren" der Knoten zu tun hat und habe im Quelltext des VST folgendes gefunden:

Zitat:

// AddChild is a compatibility method and will implicitly validate the parent node. This is however
// against the virtual paradigm and hence I dissuade from its usage.
Wenn nicht mit "AddChild", wie soll ich das sonst machen?

himitsu 27. Okt 2009 13:09

Re: VirtualStringTree: Speicherleck
 
Wie erzeugst du PNodeDate?

genau andersrum mußt du es auch wieder freigeben!


z.B.:
Delphi-Quellcode:
NodeData := GetMem(SizeOf(TNodeData));
Initialize(NodeData);
...
Finalize(NodeData^);
FreeMem(NodeData);

// oder

New(NodeData);
...
Dispose(NodeData);

guidok 27. Okt 2009 14:03

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von himitsu
Wie erzeugst du PNodeDate?

Gar nicht. Ich füge dem vst nur einen neuen Knoten hinzu.

Delphi-Quellcode:
Node := vst.AddChild(ParentNode);
Data := vst.GetNodeData(Node);
Das Problem ist eher, dass nur die Knoten im vst.OnFreeNode freigegeben werden, die auch einmal aufgeklappt waren.

sirius 27. Okt 2009 14:23

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von guidok
Das Problem ist eher, dass nur die Knoten im vst.OnFreeNode freigegeben werden, die auch einmal aufgeklappt waren.

Den Satz habe ich schonmal hier im Forum gelesen :gruebel:

Nichtsdestotrotz sollte man eh nie Datenhaltung und GUI vermischen.
Ich habe meine Klassenstruktur in denen die Daten stehen und die auch separat freigegeben wird. Dem VST gebe ich nur den entsprechenden Zeiger zu je einem Element dieser Datenstruktur.

Edit: Und dort wo ich den Satz schonmal gelesen habe, stand auch die Lösung:
Es ist vom Entwickler direkt so vorgesehen, was du beschreibst.
--> It's not a bug, it's a feature
Entweder du gehst so vor, wie ich, oder du machst es wie im Link beschrieben: Mit ValidateNode jeden Knoten anfassen, dadurch wird auch onFreeNode aufgerufen, wenn es soweit ist (--> Performanceeinbußen), oder ist bei onInitNode den Speicher belegen.

himitsu 27. Okt 2009 14:29

Re: VirtualStringTree: Speicherleck
 
Ach nee, stimmt ja, der Record wird ja direkt an die Node-Struktur angehängt

nja, zumindestens Freigeben würde ich es so, falls du mal den Record erweiterst und z.B. noch 'nen String einfügst.
Delphi-Quellcode:
Finalize(NodeData^);
Diese NodeDataSize (oder wie das nochmal hieß) hast du auch richtig gesetzt?


Hast du eventuell sowas wie FreeOnCollapse aktiviert?


[add]
ich mach es meißtens auch nur so wie sirius,
aber das liegt daran, daß ich diese Structuren eh extern hab und immer nur eine Teilmenge im Tree anzeige und davon auch nur der grade sichtbare (aufgeklappte) Teil im Tree vorhanden ist.

läßt sich auch schneller der Tree füllen, wenn da da insgesamt viel reinmacht, nur den sichtbaren Teil einfügt und den Rest, wenn nötig Stück für Stück nachläd.

guidok 28. Okt 2009 06:18

Re: VirtualStringTree: Speicherleck
 
Zitat:

Den Satz habe ich schonmal hier im Forum gelesen
Wusst ich doch, dass ich das auch schon mal gelesen hatte. Hab es nur nicht gefunden. Danke.

Zitat:

Nichtsdestotrotz sollte man eh nie Datenhaltung und GUI vermischen.
Ich habe meine Klassenstruktur in denen die Daten stehen und die auch separat freigegeben wird. Dem VST gebe ich nur den entsprechenden Zeiger zu je einem Element dieser Datenstruktur.
Darum bemühe ich mich auch, bin aber anscheinend noch nicht ganz so weit. Vielleicht könnt ihr mir ja einen Denkanstoß geben?

In diesem Fall nutze ich den Tree zur Anzeige von Einstellungen, also z.B.

|-Verzeichnisse
| |
| |------Archivdaten C:\Foo
| |------Temporäre Daten C:\Bar
|
|-Benutzerhinweise
|
|------Neue Daten vorhanden +
|------Irgendwas anderes -

Die eigentlichen Daten (also das was rechts in der zweiten Spalte steht) sind natürlich in einer anderen Datenstruktur vorhanden, allerdings kann ich da ja schlecht die Baumstrukturinformationen und die "Beschriftung" der Felder mit vorhalten, oder?

Ich habe auf das Beispiel oben bezogen sowas:

path_archiv = C:\Foo
path_temp = C:\Bar
info_new = 1
info_somthingelse = 0

Wie macht ihr das? Wie würdet ihr das machen?

hoika 28. Okt 2009 07:36

Re: VirtualStringTree: Speicherleck
 
Hallo,

Zitat:

Die eigentlichen Daten (also das was rechts in der zweiten Spalte steht) sind natürlich in einer anderen Datenstruktur vorhanden
Wie vorhanden ?
Als Klasse ?

Dann packe einen "Pointer" der Klasse in die NodeData mit rein.

Delphi-Quellcode:
type
  TInfo = class
    Text: String;
  end;


  TNodeData = class
    Id: Integer;      //Identifikation des Feldes
    AuthReq: Integer; //Benötigte Authorisierung;
    Info: TInfo;
  end;
Es ist übrigens keine svchechte Idee, statt dem Record
auch gleich eine Klasse als NodeData zu benutzen.
Vielleicht kannst du ja gleich alle benötigten Daten (Id, AuthReq)
in diese Info-Klasse packen.



Heiko

himitsu 28. Okt 2009 07:54

Re: VirtualStringTree: Speicherleck
 
Delphi-Quellcode:
TNodeData = record
  Id: Integer;      //Identifikation des Feldes
  AuthReq: Integer; //Benötigte Authorisierung;
  Text: String;     //Beschriftung
end;
Wenn man das so sieht, dann hast du es ja schon getrennt
ID ist dann wohl dein externer Datensatz
und das Andere betrifft nur die GUI/Anzeige

Du könntest natürlich auch nur die ID in .Data speichern
und dir dann noch Funktionen anlegen, welche dir Anhand der ID die Authorisierung oder die Beschriftung liefern, aber so ist es anscheinend doch auch schon recht gut getrennt,
es sei denn du nutzt schon zum Befüllen deines Records solche Funktionen, dann kannst du es natürlich auch später immernoch machen und brauchst nur die ID
Delphi-Quellcode:
function TFormX.GetTVCaption(ID: Integer): String;

guidok 28. Okt 2009 08:20

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von himitsu
Delphi-Quellcode:
TNodeData = record
  Id: Integer;      //Identifikation des Feldes
  AuthReq: Integer; //Benötigte Authorisierung;
  Text: String;     //Beschriftung
end;
Wenn man das so sieht, dann hast du es ja schon getrennt
ID ist dann wohl dein externer Datensatz
und das Andere betrifft nur die GUI/Anzeige

Stimmt genau! ID ist die Identifikation des Feldes in der "Datenhaltung".

Ein einzelner Datensatz ist bei mir ein CollectionItem und diese werden in einer Collection gehalten, insofern könnte ich für den Knoten auch einen Zeiger auf das CollectionItem angeben.

Ich sehe derzeit folgende Möglichkeiten:

1. Ich lasse es so wie es ist und validiere die Knoten beim Einfügen, um auch alle wieder entfernt zu bekommen.

2. Ich lasse ID weg und füge stattdessen gleich einen Zeiger auf das CollectionItem in den Knoten ein, was vermutlich keine schlechte Idee ist, weil ich dann direkt auf die Daten zugreifen kann ohne vorher nach der ID suchen zu müssen.

3. Ich füge nur einen Zeiger auf ein CollectionItem ein und halte alle Daten (also auch die, die ich nur für die "hübschere" Anzeige benötige) in der Collection. Das führt natürlich dazu, das ich ein paar mehr Daten sichern muss (Inifile, XML-Datei oder DB), die eigentlich nur für die GUI interessant sind. Vorteil wäre hier, dass ich ziemlich flexibel neue Werte einfügen könnte, weil das alles dynamisch aus dem Datenbestand aufgebaut wird.

4. Ich füge nur die ID ein und hole mir die Werte wie von himitsu vorgeschlagen über entsprechende Funktionen.

Darüber werde ich einmal nachdenken müssen. Vielen Dank für die Inspiration!

sirius 28. Okt 2009 09:38

Re: VirtualStringTree: Speicherleck
 
Wieso baust du deine Collection nicht so auf, wie die Baumstruktur auch aussieht?

guidok 28. Okt 2009 10:02

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von sirius
Wieso baust du deine Collection nicht so auf, wie die Baumstruktur auch aussieht?

Verstehe ich jetzt nicht. Was meinst du? Kannst du das mal näher erläutern?

Ich müsste ja für ein Item angeben unter welchen übergeordneten Knoten es angeordnet ist. Könnte ich natürlich tun und das entspräche ja ungefähr der Möglichkeit (3).

Noch mal auf das Beispiel von oben eingehend würde in den Beschreibungstexten für ein Item dann stehen:

'Verzeichnisse|Archivdaten'
'Verzeichnisse|Temporäre Daten'

usw., wobei der '|' als Separator für die Knotenebenen dienen würde. Beim Einfügen des Knotens müsste ich dann nur nach den entsprechenden übergeordneten Knoten suchen.

Da die Collection irgendwo gespeichert wird, würden damit auch die Beschreibungstexte mit gespeichert werden. Das ist nur dumm, sobald z.B. Benutzerdaten gespeichert werden, dann wird für jeden Benutzer der ganze Rattenschwanz gespeichert.

sirius 28. Okt 2009 11:35

Re: VirtualStringTree: Speicherleck
 
Ich glaube, ich habe nicht erfasst, was du eigentlich anzeigen willst.

guidok 28. Okt 2009 12:19

Re: VirtualStringTree: Speicherleck
 
Ich nehme nochmal Bezug auf das Beispiel von Post #6:

In der Collection gibt es die Items "Archivdaten", "Temporäre Daten", "Neue Daten vorhanden" und "Irgendwas anderes". Im Baum sollen also die zugehörigen Daten in der zu dem Childnode gehörigen zweiten Spalte angezeigt werden. Damit das ganze etwas hübscher und aufgeräumter daherkommt, sollen diese Knoten noch die übergeordneten Knoten "Verzeichnisse" und "Benutzerhinweise" erhalten. Zu diesen beiden Knoten gibt es nun keine eigentlichen Daten (= Items in der Collection).

+-Verzeichnisse
| |
| +------Archivdaten
| +------Temporäre Daten
|
+-Benutzerhinweise
|
+------Neue Daten vorhanden
+------Irgendwas anderes



Ich denke die beste Lösung wird es sein die CollectionItems so beizubehalten, wie geplant und in den Knotendaten die ID und einen Zeiger auf das Item zu hinterlegen. Über die Knoten-ID kann ich per Getter-Funktion den passenden GUI-Text ermitteln und über den Zeiger auf die Items die Daten für die zweite Spalte.

Um die Erstellung der GUI dennoch einfach und übersichtlich zu halten, kann ich diese ja in einer Stringkonstanten im Quelltext hinterlegen, die von der o.g. GUI-Klasse "aufgedröselt" wird.

So in etwa:

Delphi-Quellcode:
const
  //Anzeigetexte für die GUI im Format ID|AuthReq|Knotentexte
  CSettingsTree  = '10101|0|Verzeichnisse|Archivdaten,' +
                    '10102|0|Verzeichnisse|Temporäre Daten,' +
                    '10201|0|Benutzerhinweise|Neue Daten vorhanden,' +
                    '10202|0|Benutzerhinweise|Irgendwas anderes';

sirius 28. Okt 2009 15:40

Re: VirtualStringTree: Speicherleck
 
Ich schlage dir mal etwas vor:
Delphi-Quellcode:
type

  TBasicCollection=class;

  //Grundlegende abstrakte Klasse für einen Knoten (egal welcher)
  TBasicCollectionItem=class(TCollectionItem)
     Destructor Destroy; override;
    private
     FChildCollection:TBasicCollection; //Für Kindknoten oder nil wenn keine Kinder
    public
     function GetVSTBezeichner:string; virtual; abstract; //Text für VST (wird je nach Ableitung anders implementiert)
     property ChildCollection:TBasicCollection read FChildCollection;
     procedure Init(const Param:string); virtual; abstract; //Param bekommt einen Teilstring von CSettingsTree
  end;

  TBasicCollection=class(TCollection)
   private
    function GetBasicCollectionItem(index:Integer): TBasicCollectionItem;
   public
    property BasicCollectionItem[index:Integer]:TBasicCollectionItem
      read GetBasicCollectionItem;
    procedure Init(const Param:string);
  end;


  //CollectionItem für die Knoten (Verzeichnisse und Benutzerhinweise)
  TCollectionItemA=class(TBasicCollectionItem)
   private
    FName: string; //hier nur Name merken
    procedure SetName(const Value: string);
   public
    function getVSTBezeichner:string; override; //result:=FName;
    property Name:string read FName write SetName;
    procedure Init(const Param:string); override;
  end;

  //CollectionItem für die anderen Knoten
  TCollectionItemB=class(TBasicCollectionItem)
   private
    FAuthReq: Integer;
    FxID: Integer;
    FText: string;
    procedure SetAuthReq(const Value: Integer);
    procedure SetText(const Value: string);
    procedure SetxID(const Value: Integer);
   public
    function getVSTBezeichner:string; override; //result:=Ftext;
    property xID:Integer read FxID write SetxID;
    property AuthReq:Integer read FAuthReq write SetAuthReq;
    property Text:string read FText write SetText;
    procedure Init(const Param:string); override;
  end;


implementation

{$R *.dfm}

//gibt den Pos-ten Teilstring in den spitzen Klammern zurück (ohne Beachtung von inneren Klammern)
function GetStringPart(const Str: string;
  Pos: Integer): String;
var aPos,ePos:Integer;
    len:Integer;
    countopen:Integer;
begin
  len:=length(str);
  aPos:=0;
  countopen:=0;
  while Pos>0 do
  begin
    inc(aPos);
    while (aPos<=len)and((Str[aPos]<>'<')or(countopen>0)) do
    begin
      case Str[aPos] of
        '<': inc(countopen);
        '>': dec(countopen);
      end;
      inc(aPos);
    end;
    inc(countopen);
    dec(Pos);
  end;

  ePos:=aPos+1;
  countopen:=0;
  while (ePos<=len)and((Str[ePos]<>'>')or(countopen>0)) do
  begin
    case Str[ePos] of
      '<': inc(countopen);
      '>': dec(countopen);
    end;
    inc(ePos);
  end;

  result:=copy(str,aPos+1,ePos-aPos-1);
end;



{ TBasicCollection }

function TBasicCollection.GetBasicCollectionItem(index:Integer): TBasicCollectionItem;
begin
  result:=items[index] as TBasicCollectionItem;
end;

procedure TBasicCollection.Init(const Param: string);
var i:Integer;
    Part:string;
    BasicCollectionItem:TBasicCollectionItem;
begin
  i:=1;
  Part:=GetStringPart(Param,i);
  while Part<>'' do
  begin
    BasicCollectionItem:=Add as TBasicCollectionItem;
    BasicCollectionItem.Init(Part);
    inc(i);
    Part:=GetStringPart(Param,i);
  end;
end;


{ TBasicCollectionItem }

destructor TBasicCollectionItem.Destroy;
begin
  FChildCollection.Free;
  inherited;
end;

{ TCollectionItemA }

function TCollectionItemA.getVSTBezeichner: string;
begin
  result:=FName;
end;

procedure TCollectionItemA.Init(const Param: string);
begin
  Name:=GetStringPart(Param,1);
  FChildCollection:=TBasicCollection.Create(TCollectionItemB);
  FChildCollection.Init(GetStringPart(Param,2));
end;

procedure TCollectionItemA.SetName(const Value: string);
begin
  FName := Value;
end;


{ TCollectionItemB }

function TCollectionItemB.getVSTBezeichner: string;
begin
  result:=FText;
end;


procedure TCollectionItemB.Init(const Param: string);
begin
  xID:=StrToInt(GetStringPart(Param,1));
  AuthReq:=StrToInt(GetStringPart(Param,2));
  Text:=GetStringPart(Param,3);
end;

procedure TCollectionItemB.SetAuthReq(const Value: Integer);
begin
  FAuthReq := Value;
end;

procedure TCollectionItemB.SetText(const Value: string);
begin
  FText := Value;
end;

procedure TCollectionItemB.SetxID(const Value: Integer);
begin
  FxID := Value;
end;
In der Anwendung:
Delphi-Quellcode:
//habe mal die Konstante etwas geändert; macht das Parsen einfacher;
//wäre aber auch mit deinem String gegangen
const CSettingsTree='<'+
                    ' <Verzeichnisse>'+
                    ' <'+
                    ' <'+
                    '  <10101><0><Archivdaten>'+
                    ' >'+
                    ' <'+
                    '  <10102><0><Tempöräre Daten>'+
                    ' >'+
                    ' >'+
                    '>'+
                    '<'+
                    ' <Benutzerhinweise>'+
                    ' <'+
                    ' <'+
                    '  <10201><0><Neue Daten vorhanden>'+
                    ' >'+
                    ' <'+
                    '  <10202><0><Irgendetwas>'+
                    ' >'+
                    ' >'+
                    '>';

type
 TForm1 = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    procedure FormCreate(Sender: TObject);
    procedure VirtualStringTree1GetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FDaten:TBasicCollection;
    procedure vstInit(Node:PVirtualNode;BasicCollection:TBasicCollection);
  public
    { Public-Deklarationen }
  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDaten:=TBasicCollection.Create(TCollectionItemA);
  FDaten.Init(CSettingsTree); //CSettingsTree in die entsprechende Collectionstruktur umformen
  vstInit(nil,FDaten); //rekursiv den VST erstellen
end;

procedure TForm1.vstInit(Node: PVirtualNode;
  BasicCollection: TBasicCollection);
var i:Integer;
    BasicCollectionItem:TBasicCollectionItem;
    cNode:PVirtualNode;
begin
  for i:=0 to BasicCollection.Count-1 do //für alle items in der Collection
  begin
    BasicCollectionItem:=BasicCollection.BasicCollectionItem[i]; //getitem
    cNode:=VirtualStringTree1.AddChild(Node,BasicCollectionItem); //Knoten erstellen
    if assigned(BasicCollectionItem.ChildCollection) then //wenn Kinder vorhanden
      vstInit(cNode,BasicCollectionItem.ChildCollection); //Kindknoten rekursiv anlegen
  end;
end;

procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
begin
  //egal welcher Knoten, überall befinden sich Ableitungen von TBasicCollectionItem in NodeData
  //und alle haben die Methode GetVSTBezeichner überschrieben
  Celltext:=
   (TObject(Sender.GetNodeData(Node)^)as TBasicCollectionItem).getVSTBezeichner;
end;

//hier noch ein Beispiel, wie man weitere Informationen je Knoten bekommt
procedure TForm1.VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
var obj:TObject;
begin
  obj:=TObject(Sender.GetNodeData(Node)^);
  if obj is TCollectionITemA then
    showmessage('Root')
  else
  if obj is TCollectionitemB then
    showmessage(Format('ID: %d; AuthReq: %d; Text: %s',
      [TCollectionItemB(obj).xID,
       TCollectionItemB(obj).AuthReq,
       TCollectionItemB(obj).Text]));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FDaten.Free;
end;
Aus der Konstante wird folgende Collection aufgebaut:
Code:
TBasicCollection
|
+-TCollectionItemA(Verzeichnisse)
| |
| +-TBasicCollection
|   |
|   +--TCollectionItemB(10101|0|Archivdaten)
|   +--TCollectionItemB(10102|0|Temporäre Daten)
|
+-TCollectionItemA(Benutzerhinweise)
  |
  +-TBasicCollection
    |
    +--TCollectionItemB(10201|0|Neue Daten vorhanden)
    +--TCollectionItemB(10202|0|Irgendetwas)
Damit kannst du quasi in BasicCollectionItem alles das implementieren, was du für den VST brauchst (neben dem NodeText kann das durchaus auch noch Checkstate o.ä. sein, was man recht simpel über die Ereignisse des VST mit setzen kann.
Die Ableitungen von BasicCollectionItem berherbegen dann zusätzliche Informationen zum Knoten.

generic 28. Okt 2009 16:55

Re: VirtualStringTree: Speicherleck
 
Das Speicherleck kommt von dem String Datentyp.
Ich habe das in dem Stammtisch Video erklärt.

2 Möglichkeiten
1) Shortstrings verwenden für die VST Struktur.
2) Den Huge String freigeben im OnFreeNode

Ich empfehle wie in dem Video gezeigt mit Objekten und nicht mit Records zu arbeiten.
http://www.delphipraxis.net/video#Stammtisch_2

guidok 30. Okt 2009 06:22

Re: VirtualStringTree: Speicherleck
 
Danke Sirius! Das ist mal ne ausführliche Lösung.

Ich werde es mir mal genau anschauen, wenn ich ein paar freie Minuten habe, aber das grundlegende Prinzip habe ich kapiert. Ich muss dann nur schauen, wie ich es mit meiner Datenhaltung in Einklang bringen kann.

himitsu 30. Okt 2009 06:31

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von generic
Das Speicherleck kommt von dem String Datentyp.

Er gibt doch aber den String frei und demnach sollte dort auch kein Speicherleck entstehen? :gruebel:
Zitat:

Delphi-Quellcode:
procedure TSettingsForm.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
...
Finalize(NodeData^.Text);

Finalize(NodeData^.Text) sollte NodeData^.Text := ''; entsprechen.

guidok 1. Nov 2009 08:39

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von himitsu
Zitat:

Zitat von generic
Das Speicherleck kommt von dem String Datentyp.

Er gibt doch aber den String frei und demnach sollte dort auch kein Speicherleck entstehen? :gruebel:
Zitat:

Delphi-Quellcode:
procedure TSettingsForm.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
...
Finalize(NodeData^.Text);

Finalize(NodeData^.Text) sollte NodeData^.Text := ''; entsprechen.

Stimmt himitsu. Ob
Delphi-Quellcode:
Finalize(NodeData^.Text);
oder
Delphi-Quellcode:
NodeData^.Text := '';
das Ergebnis ist das selbe.

Aber:

VT.OnFreeNode wird nur für die Knoten aufgerufen, die auch zur Laufzeit einmal auf dem Bildschirm sichtbar (also aufgeklappt) waren. Daher wird nur ein Teil der Strings freigegeben.

alzaimar 1. Nov 2009 09:05

Re: VirtualStringTree: Speicherleck
 
Leute, was habt ihr eigentlich immer mit eurem 'Finalize'?
New/Dispose (bei Records) bzw. Create/Free (bei Klassen) kümmert sich doch um die ganze Chose. Deshalb programmiert man doch in Delphi und nicht in C...? Es wurde doch lang und breit erklärt, das dieses OnFreeNode nicht dazu dient, den Speicher freizugeben. Man muss das also selbst machen.

Ich würde mir auch keinen abbrechen, um die Information, die baumartig dargestellt werden soll, baumartig ablegen. Die Zeit hab ich einfach nicht:

Wenn ich in einem Baum polymorphe Informationen darstelle, klebe ich an jeden Knoten ein Objekt. Das Freigeben mache ich selbst, indem ich jedes Objekt in eine TObjectlist packe (OwnsObjects := True).

Was habe ich an dem Problem nicht verstanden, das es in meinen Augen keins ist? Außer, das ich den Sinn und Zweck dieses Events nicht verstanden habe.

himitsu 1. Nov 2009 09:48

Re: VirtualStringTree: Speicherleck
 
@alzaimar:
Hier war der Record schon im Tree eingebaut.

New/Dispose reservieren Speicher, geben ihn wieder frei und machen zusätzlich noch die Initialisierung/Finalisierung.

Da der Speicher vom Record aber schon vorhanden war, fehlt hier nur noch die Initialisierung/Finalisierung.

Und eben eine stelle, wo die Finalisierung auch aufgerufen wird.

OnFreeNode scheint ja demnach wohl nicht so gut geeignet, obwohl ich das einfach nicht versteht, warum dieses nur aufgerufen wird, wenn der Node erst angezeigt wurde o.O


aber ich hätte da jetzt eine "Lösung":
Delphi-Quellcode:
Node := vst.AddChild(ParentNode);
Data := vst.GetNodeData(Node);
Include(Node.States, vsInitialUserData);
Initialize(Node);
...
Data.Text := 'gfdsvcxvy';
und schon sollte OnFreeNode aufgerufen werden :angel:
Delphi-Quellcode:
procedure TSettingsForm.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
...
Finalize(NodeData^.Text);

guidok 1. Nov 2009 11:33

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von alzaimar
Leute, was habt ihr eigentlich immer mit eurem 'Finalize'?
New/Dispose (bei Records) bzw. Create/Free (bei Klassen) kümmert sich doch um die ganze Chose. Deshalb programmiert man doch in Delphi und nicht in C...? Es wurde doch lang und breit erklärt, das dieses OnFreeNode nicht dazu dient, den Speicher freizugeben. Man muss das also selbst machen.

Ich würde mir auch keinen abbrechen, um die Information, die baumartig dargestellt werden soll, baumartig ablegen. Die Zeit hab ich einfach nicht:

Wenn ich in einem Baum polymorphe Informationen darstelle, klebe ich an jeden Knoten ein Objekt. Das Freigeben mache ich selbst, indem ich jedes Objekt in eine TObjectlist packe (OwnsObjects := True).

Was habe ich an dem Problem nicht verstanden, das es in meinen Augen keins ist? Außer, das ich den Sinn und Zweck dieses Events nicht verstanden habe.

Warum so aggresiv?

Ich habe das Problem schon verstanden und arbeite an der für mich besten Lösung dazu, ob nun mit Records oder mit Objekten soll mal meine Sorge sein.

OnFreeNode dient meiner Meinung nach schon dazu, Daten im Record freizugeben, die dessen bedürfen (so steht es zumindest in einigen Tutorials zum VT). Überrascht wurde ich nur davon, dass anscheinend nicht unbedingt alle eingefügten Knoten auch in OnFreeNode freigegeben werden und das kann durchaus auch anderen als mir passieren.

alzaimar 1. Nov 2009 12:57

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von guidok
Warum so aggresiv?

Verwundert. Nur verwundert.

Ich habe übrigens Finalize noch *nie* in meinen Projekten verwendet. Und ich programmiere seit D2.

himitsu 1. Nov 2009 13:13

Re: VirtualStringTree: Speicherleck
 
Hier bot sich Finalize halt an, da ja praktisch ein Record in einen anderen Injeziert wurde,
welcher aber vom Aufbau des injezierten Records keine Ahnung hat.

sirius 2. Nov 2009 18:01

Re: VirtualStringTree: Speicherleck
 
Zitat:

Zitat von himitsu
Delphi-Quellcode:
Include(Node.States, vsInitialUserData);
Initialize(Node);

Dafür gibt es ja (wie vom Entwickler beschrieben) TBaseTreeview.ValidateNode :zwinker:


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