Wann speichert man?

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Wann speichert man?

Beitrag von klausi1305 »

Servus,

ich arbeite auf einer Form und speichere, lösche da Werte von Instanzen eines Objektes oder generiere neue Instanzen. Dies realisiere ich über eine Objectlist. Wann ist es sinnvoll die Werte der verschiedenen Instanzen des ObjectList in eine Datei per FileStream zu speichern und oder zu laden?

Nur beim OnCreate Laden und bei OnClose Speichern? oder jedes mal wenn innerhalb der ObjectList verändert werden?

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Wann speichert man?

Beitrag von Christian »

Das kommt drauf an wie oft dein programm und oder das betriebsystem abstürzt.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2816
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Wann speichert man?

Beitrag von m.fuchs »

Ganz klare Antwort: du solltest alle 23,42 Sekunden speichern. :wink:

Spaß beseite. Für ein paar Ratschläge zu diesem Thema müsste man mehr wissen.
Was macht dein Programm? Wie interagiert es mit dem Benutzer? Was erwarten deine Benutzer vom Verhalten des Programms? Wie aufwändig ist es die Änderungen nachzuvollziehen? etc.

Und ja, auch die Frage nach der Wahrscheinlichkeit von Abstürzen spielt eine Rolle.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Re: Wann speichert man?

Beitrag von klausi1305 »

Ich speichere zig Eingabewerte einer Instanz in die Objectliste oder erstelle neue oder lösche . Parallel werden die Instanzen des Objects in einer Listbox angezeigt (da gibts aber noch nen kleinen Anfängerfehler drinne den ich nicht so richtig finde :oops: )
. Nun rätzel ich grad wann ich die Objectliste speichere in den Filestream.
Das das Programm absturzsicher ist, ist eigentlich mein Hauptanspruch...und das es funktioniert

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2816
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Wann speichert man?

Beitrag von m.fuchs »

Das ist immer noch recht unspezifisch. Was für ein Programm ist das denn überhaupt, was für Werte sind das?

Die Frage nach dem Zeitpunkt der Speicherung ist ja weniger ein Programmierproblem, da müsstest du schon ein wenig mehr Informationen liefern.

Ein paar Beispiele:
  • Wenn deine ganzen Werte nur Control- und Fensterpositionen enthalten, dann speichere halt zu Programmende. Gibt es zwischendurch einen Absturz: egal, ist ja nix Wichtiges verlorengegangen.
  • Gibt ein User die Werte per Hand ein, wäre ein häufigeres Abspeichern sinnvoll.
  • Werden die Werte häufig verändert dürfte eine Versionierung nicht verkehrt sein.
  • etc.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Re: Wann speichert man?

Beitrag von klausi1305 »

Es ist nur nen Einheiteneditor für Spielfiguren....
ja, der User gibt die Werte per Hand ein.....Ich habe mich hier nur auf den Namen beschränkt, da nur dieser in der Listbox angezeigt werden soll....
Jedoch passiert ab dem 2. Erzeugen das die Listbox 2x den Namen des 2. Objects anzeigt usw. bei weiteren Versuchen..... :?:

Hier wird die Figur als Object erzeugt und in die Objectliste (Einheitliste) gespeichert

Code: Alles auswählen

procedure TFEinheiteditor.EinheitSpeichern;
begin
   If Edit1.Text ='' then Exit;
   Einheit := TEinheit.Create;
   Einheit.Einheittypname:=Edit1.Text;
   Einheitliste.Add(Einheit);
   EinheitlisteSpeichern;  //Speichern der Liste in Filestream
   Einheit.Free;
   Listboxaktuell; //Aktualisieren der Listbox
end;
Speichern der Objectliste

Code: Alles auswählen

procedure TFEinheiteditor.EinheitlisteSpeichern;
Var
  Zaehler      : Integer;
  Test         : Integer;
  FileStream1  : TFilestream;
  Len1         : Cardinal;
  Str          : String;
  begin
      Filestream1 := TFilestream.Create('Basisunit.bui', fmCreate); //
     Try
        Zaehler := -1;
        Test :=Einheitliste.Count;
        Repeat
          Inc(Zaehler);
          Einheit := TEinheit(Einheitliste.Items[Zaehler]);
          //Einheit Name
          Str   := Einheit.Einheittypname;
          Len1  := Length(Str);
          FileStream1.WriteBuffer(Len1,SizeOf(Len1));
          FileStream1.WriteBuffer(Str[1],Len1);
         until Zaehler = Test-1;
     finally
      Filestream1.Free;
  end;
end; 
Laden der Liste aus Stream

Code: Alles auswählen

procedure TFEinheiteditor.EinheitlisteLaden;
Var
  Zaehler           : Integer;
  FileStream1       : TFilestream;
  Len1              : Cardinal;
  Test              : Integer;
  Str               : String;
begin
    Filestream1 := TFilestream.create('Basisunit.bui',fmOpenRead);
    Zaehler := -1;
    Test := Einheitliste.Count;
 
    Try
      Repeat
         Inc (Zaehler);
         Einheit := TEinheit(Einheitliste.Items[Zaehler]) ;
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Einheit.Einheittypname:=Str;
       until Zaehler = Test-1;
     finally
      Filestream1.Free;
    end;
end;    
Aktualisieren der Lìstbox

Code: Alles auswählen

procedure TFEinheiteditor.ListboxAktuell;
//Liest von allen Instanzen Einheit den Namen und added diese in Listbox1
Var
  Zaehler : Integer;
  Test : Integer;
begin
   Listbox1.Clear;
   EinheitlisteLaden; //----------------------------------------------
   Test := Einheitliste.Count;
   Zaehler :=-1;
   Repeat
     Inc(Zaehler);
     Einheit := TEinheit(Einheitliste[Zaehler]);
     Listbox1.Items.Add(Einheit.Einheittypname);
   Until Zaehler = Test-1;
end;                          

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2816
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Wann speichert man?

Beitrag von m.fuchs »

*seufz*

Bitte keinen Quellcode. Beantworte doch einfach meine Fragen, erzähl etwas über dein Programm.
Vor dem Programmieren sollte erst einmal klar werden was passieren soll.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Re: Wann speichert man?

Beitrag von Euklid »

klausi1305 hat geschrieben:Nur beim OnCreate Laden und bei OnClose Speichern? oder jedes mal wenn innerhalb der ObjectList verändert werden?
Was spricht dagegen?

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Wann speichert man?

Beitrag von Michl »

Euklid hat geschrieben:Was spricht dagegen?
scheinbar das: :wink:
klausi1305 hat geschrieben:da gibts aber noch nen kleinen Anfängerfehler drinne den ich nicht so richtig finde

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Re: Wann speichert man?

Beitrag von klausi1305 »

Ich weiß jetzt nicht ob ich nen neuen Thread dazu aufmachen soll, aber es hat ja immer noch mit dem Speichern und Laden von meinem FIlestream zutuen :D

Also folgendes Problem.

Ich erstelle verschiedene Instanzen von TEinheit (eigene Klasse), speichere diese in eine TObjectlist, welche im OnCreate erzeugt wird und beim OnClose gespeichert wird. Bei OnClose speichere ich diese per Filestream in eine Datei.
Die Datei enthält als erste Position die Anzahl der gespeicherten Instanzen. (Da ich nich weiß wie ich nen unbekannt langen Filestream lesen kann, sprich ich hatte kein Abbruchkriterium für meine Schleife)

Wenn ich öffne wird die TObjectlist aus dem Filestream geladen im OnCreate.
Die Anzahl der beinhalteten Instanzen von TEinheit werden korrekt ermittelt....
Doch hier ist das Problem; es werden keine Instanzen geladen....Er versucht ja nicht mal einen Wert zu ermitteln, obwohl alle Werte in der Datei stehen :(

Gruß Sebastian

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Wann speichert man?

Beitrag von Michl »

Es ist wirklich schwierig, dir zu antworten. Es kann alles Mögliche ursächlich für dein "Ladeproblem" sein.

Versuche doch einmal, das Problem auf ein Minimalbeispiel zu reduzieren und versuche es damit. Falls du dann nicht weiter kommst, postest du hier dein Code (evtl. als Zip) und ich bin mir sicher, dass dir hier viele Leute gerne helfen werden.

Raten bzw. bruchstückhaftes Lesen von Quellcode (siehe oben), ob sich evtl. irgendwo ein Fehler eingeschlichen haben könnte, bringt dir und uns nicht wirklich was.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Re: Wann speichert man?

Beitrag von klausi1305 »

Ich habe mal mein Problem minimiert...

Es ist immer noch das Problem, das zwar die Anzahl der Instanzen aus dem Stream gelesen wird, jedoch nicht die Instanzen selbst (Procedure EinheitenlisteLaden):?:
Zuletzt geändert von klausi1305 am So 18. Aug 2013, 21:37, insgesamt 1-mal geändert.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Wann speichert man?

Beitrag von Michl »

Ok, habs mir mal runtergeladen.

Generell günstig ist, zum Fehlerfinden einen Breakpoint zu setzen, dann Starten (F9) -> einen Befehl weiter (F8) ... solange F8 drücken, bis Fehlermeldung kommt. Diese Fehlermeldung kann man dann gezielt bearbeiten...

Ich habe mal Form1.OnCreate bearbeitet:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
Var
  f : TFilestream;
begin
   Einheitliste :=TObjectlist.create(True);     //Einheitliste erzeugen
   // Wenn Einheitendatei fehlt, leere Datei erstellen
   If FileExists('Basisunit.bui') then
     EinheitlisteLaden
   else
     Begin
       f := TFilestream.Create('Basisunit.bui', fmcreate);
       f.free;
     end;
end
und "EinheitlisteLaden" aufgerufen (wolltest du doch?!).


Dann habe ich Breakpoint in "TForm1.EinheitlisteLaden" gesetzt und mit F8 den ersten Fehler gesucht -> du hattest

Code: Alles auswählen

         Einheit := TEinheit(Einheitliste.Items[Zaehler]) ; 
aufgerufen, ohne zuvor "Items[Zaehler]" erstellt zu haben. Ich habe daher einfach "Einheitliste.Add(Einheit);" vom Schluss der Procedure nach vorn genommen und es funktioniert:

Code: Alles auswählen

procedure TForm1.EinheitlisteLaden;
//Lädt alle Instanzen von Einheit aus Filestream und added in Einheitliste
Var
  Zaehler           : Integer;
  FileStream1       : TFilestream;
  Len1              : Cardinal;
  Anzahl            : Integer;
  Str               : String;
begin
  Filestream1 := TFilestream.create('Basisunit.bui',fmOpenRead);
    Try
        //Auslesen der Anzahl der Einheiten  in Basisunit.bui
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Anzahl:=StrToInt(Str);
         Zaehler:=-1;
//      Showmessage(Str);
     Repeat
         Inc(Zaehler);
         Einheit := TEinheit.Create;                    //Testweise...Kein erfolg ob mit oder ohbe
 
         Einheitliste.Add(Einheit);
 
         Einheit := TEinheit(Einheitliste.Items[Zaehler]) ;
         //Einheit Name                                     //ab hier springt es bis Filestream.Free?????
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Einheit.Einheittypname:=Str;
         //Einheit Typ
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Einheit.Einheittyp:=StrToInt(Str);
         // Einheit wird geadded
//         Einheitliste.Add(Einheit);
       Until Zaehler = Anzahl-1;
     finally
      Filestream1.Free;
    end;
  ListboxAktuell;
end;
Um die geladenen Daten anzuzeigen musst du natürlich noch "ListboxAktuell;" aufrufen (s.o.).

Mehr habe ich jetzt nicht probiert...

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

klausi1305
Beiträge: 35
Registriert: Mo 1. Jul 2013, 21:30
OS, Lazarus, FPC: Win 7 Laz 1.0.10
CPU-Target: 32 Bit
Wohnort: Leipzig

Re: Wann speichert man?

Beitrag von klausi1305 »

Ich war fest der Überzeugung, das ich eine Instanz erst erzeugen muss, diese befüllen und dann in die TObjectlist adde....seltsam..

Riesen Dank Michl!!!

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Wann speichert man?

Beitrag von Michl »

klausi1305 hat geschrieben:Ich war fest der Überzeugung, das ich eine Instanz erst erzeugen muss, diese befüllen und dann in die TObjectlist adde....seltsam..
Erzeugen muss man sie ja auch! Mit

Code: Alles auswählen

Einheit := TEinheit.Create;
erzeugst du diese und stellst den dafür notwendigen Speicher zur Verfügung. Füllen kann man sie erst danach.

[Edit]
Folgende Zeile macht dann eigentlich auch keinen Sinn

Code: Alles auswählen

         Einheit := TEinheit(Einheitliste.Items[Zaehler]) ; 
,da "Einheit" schon den Zeiger auf genau diesen Speicher hat, wäre also gleichbedeutend mit

Code: Alles auswählen

         Einheit := Einheit;
Um den Code gut lesen zu können würde ich das eigentlich so machen:

Code: Alles auswählen

procedure TForm1.EinheitlisteLaden;
//Lädt alle Instanzen von Einheit aus Filestream und added in Einheitliste
Var
  Zaehler           : Integer;
  FileStream1       : TFilestream;
  Len1              : Cardinal;
  Anzahl            : Integer;
  Str               : String;
begin
  Filestream1 := TFilestream.create('Basisunit.bui',fmOpenRead);
    Try
        //Auslesen der Anzahl der Einheiten  in Basisunit.bui
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Anzahl:=StrToInt(Str);
         Zaehler:=-1;
//      Showmessage(Str);
     Repeat
         Inc(Zaehler);
         Einheit := TEinheit.Create;
 
         //Einheit Name
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Einheit.Einheittypname:=Str;
         //Einheit Typ
         Filestream1.ReadBuffer(Len1,SizeOf(Len1));
         SetLength(Str,Len1);
         Filestream1.ReadBuffer(Str[1], Len1);
         Einheit.Einheittyp:=StrToInt(Str);
 
        // Einheit wird geadded
         Einheitliste.Add(Einheit);
 
       Until Zaehler = Anzahl-1;
     finally
      Filestream1.Free;
    end;
  ListboxAktuell;
end;
Sprich: Einheitliste.Add zum Schluss, wie du das hattest, den unnötigen Zugriff auf die Liste weg!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Antworten