| Autor |
Beitrag |
catweasel
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 20:44
Hi,
ich habe folgendes Problem:
Ich möchte eine selbst erzeugt Komponente freigeben....
Gut. Ok. .. Das macht man normalerweise mit
Quelltext
Aaaaber, es wird ja schon in der Delphi Hilfe ausdrücklich darauf hingewiesen, das man soetwas tunlichst NICHT in einem der Eventhandler des Objekts machen sollte.......
Was aber wenn genau das erwünscht ist
Ich möchte eine Komponente die sich auf Klick freigibt....
(Ich bastel an einem kleinen Memory Spiel. Jede Karte ist ein Objekt und wenn nach einem Klick ein Paar aufgedeckt ist, werden die Karten freigegeben.. Sind ja nun überflüssig)...
Ich könnte natürlich dem Typ TKarte eine bool Eigenschaft "purge" oder so mitgeben die True wird wenn die Karte weggehauen werden kann und ein Timer überprüft ständig ob es Karten mit purge = true gibt....
Das würd ich aber gerne Vermeiden, da ich Timer nicht besonders mag und mir diese Lösung auch nicht sehr astrein vorkommt...
Ahja: Wenn man trotzdem eine Komponente freigibt z.b. ein Button.free im eigenen onClick Ereignis, dann kann es zu einer Exception kommen.... MUSS aber nicht....
Manchmal gehts gut, manchmal nicht....
Was genau ist da eigentlich das Problem damit wenn eine Komponente sich selbst freigeben will und kann man für Komponenten auch ein "release" implementieren wie für Forms, wo ja der MessageQueue vor dem destroy abgearbeitet wird....???
und nocheins:
Diese Accessviolation Exception hat ja ansonsten keinerlei (sichtbaren) Einfluss auf den Ablauf des Programms.. Es ist nur nervig..
wenn ich nun versuche die Exception abzufangen mit TRY & EXCEPT, dann reagiert das Programm überhaupt nicht darauf. Die Exception wird nicht abgefangen... warum...?
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Di 23.12.03 21:08
Ich vermute mal, dass es vorkommen kann, dass die Komponente freigegeben wurde, dann aber noch versucht wird, auf properties/Methoden der Komponente zuzugreifen. Dann kommt es zwangsläufig zu einer Exception. Wenn du aber nach dem Freigeben ein
exit; einfügst, sollte es funktionieren, ich bin mir jetzt aber nicht sicher.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 21:12
Das war auch meine Vermutung zuerst.
Und das Exit; habe ich auch schon da stehen, aber kommt immernoch eben zu den exceptions....
Auch wenn das Free definitiv das letzte ist was im Leben der Komponente passiert...
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
TomT
      
Beiträge: 116
Suse 9.1 WinXP
D6 Pers
|
Verfasst: Di 23.12.03 21:17
Auf Anhieb würd ich sagen, mach deine Komponente doch einfach unsichtbar. Ist auf den ersten Blick zwar nicht die sauberste Möglichkeit, erspart dir aber den Ärger da irgendwas drumrum zu schreiben, was wieder hunterte andere Fehlermöglichkeiten bietet.
_________________ ...und da wurde mir klar, dass eine Toolbar keine Kneipe für Heimwerker ist.
|
|
TomT
      
Beiträge: 116
Suse 9.1 WinXP
D6 Pers
|
Verfasst: Di 23.12.03 21:26
Oh, fast vergessen. Die Komponente ruft beim .free alle Destruktoren aller Komponenten auf, von denen deine Komponente erbt. Diese Exceptions kannst du nicht abfangen (meines Wissens). Das heißt, das falls da noch irgendeine Ereignismethode abläuft die zum Click eines geerbten Steuerelements gehört diese unter diesen Umständen ins Leere.
Zum Beispiel, wenn du einen Button freigibst die Zeichenfunktion läuft aber noch.
_________________ ...und da wurde mir klar, dass eine Toolbar keine Kneipe für Heimwerker ist.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 22:31
Quelltext 1:
| Auf Anhieb würd ich sagen, mach deine Komponente doch einfach unsichtbar. |
hatte ich zunächst auch vorgehabt, aber die Prüfung ob es das letzte paar ist => Spielende, müsste ich dann wieder über eine "gehört-zu-einem-gefundenen-paar" Eigenchaft bei jedem Klick für jede Karte prüfen....
Durch das entfernen der Komponente muss ich nur noch suchen ob eine Komponente vom Typ TKarte existiert.. wenn nicht (mehr).. => Spielende
Daher der Ansatz mit dem Komponentenlöschen......
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Di 23.12.03 22:47
erledige das freigeben doch in einer Fkt außerhalb der komponente. Du setzt in der Komponente ein Flag "gelöscht" und dann werden alle kompos mit diesem Flag beseitigt.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 23:20
| Zitat: | | erledige das freigeben doch in einer Fkt außerhalb der komponente. Du setzt in der Komponente ein Flag "gelöscht" und dann werden alle kompos mit diesem Flag beseitigt. |
Ja, diese Möglichkeit habe ich in meinem ersten Posting auch angerissen...
Aber ich bräuchte eben einen Timer der das immerwieder checkt.....
Und ich mag keine Timer
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Di 23.12.03 23:23
Nicht unbedingt... du könntest als Flag Visible auf false setzen und dann im OnClick der Buttons als erstes alle Unsichtbaren Buttons löschen.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 23:28
| Zitat: | | Nicht unbedingt... du könntest als Flag Visible auf false setzen und dann im OnClick der Buttons als erstes alle Unsichtbaren Buttons löschen. |
Wobei ich jeden Button auf visibility prüfen muss 
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Di 23.12.03 23:32
na, das dürfte doch kein Problem sein
Delphi-Quelltext 1: 2: 3: 4:
| for i := form1.componentcount-1 downto 0 do if form1.components[i] is TMemoryButton then if not TMemoryButton(form1.components[i]).visible then ... |
(hab ich aber nicht getestet)
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Di 23.12.03 23:37
Ja, das das geht ist einigermassen klar..
Da ich die Memorykarten als "array of TKarte" deklariere, kann ich sogar direkt über Kartenset[x].visible darauf zugreifen.
Der Punkt ist hier die Performance.... Bei einem MemorySet aus 200 Bildern sind das 400 Karten.... Das dauert schon eine Weile....
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
recall
      
Beiträge: 449
|
Verfasst: Mi 24.12.03 00:08
Was hast du gegen Timer ?
Du kannst doch einen in der OnClick-Routine erstellen:
Delphi-Quelltext 1: 2: 3: 4: 5:
| Timer1 := TTimer.Create(Self ); Timer1.Interval := 1; Timer1.Enabled := True; Timer1.OnTimer := ... ... | oder so ähnlich. Wieso aber nicht ein Design-Time-Timer ?
Einfacher ist der Timer doch allemal !?!
Viele Grüsse.
//EDIT: Du bräuchtest doch keinen Timer, der das immer wieder checkt.
Nehmen wir an du hast EINEN Timer in deinem Projekt (Enabled := False):
Global Deklariert:
Delphi-Quelltext 1: 2:
| var Obj: TWinControl=nil; |
Im OnClick der Komponente, die gelöscht werden soll:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| if Sender is TWinControl then begin Obj := (Sender as TWinControl); Timer1.Interval := 1; TImer1.Enabled := True; end; |
Im OnTimer:
Delphi-Quelltext 1: 2: 3:
| if Obj<>nil then Obj.Free; Timer1.Enabled := False; |
Und ferddig.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Mi 24.12.03 01:36
| Zitat: | Was hast du gegen Timer ?
Du kannst doch einen in der OnClick-Routine erstellen: |
Ein Timer ist nicht zuverlässig.
Ich kann nicht exakt entscheiden was das ontimer ereignis ausgeführt wird . Damit meine ich das Timerereignis selbst.
Ein Timer funktioniert ja über Nachrichten und da kann schon mal eine verlorengehen bzw. sehr spät ankommen.
Ein Timer verbraucht viel resourcen.
Ein Timer zu benutzen ist kein guter OOP Stil.
Gab hier in anderen Threads schon diverse Diskussionen über Timer
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
TomT
      
Beiträge: 116
Suse 9.1 WinXP
D6 Pers
|
Verfasst: Mi 24.12.03 11:24
| Zitat: | Ein Timer funktioniert ja über Nachrichten und da kann schon mal eine verlorengehen bzw. sehr spät ankommen.
Ein Timer verbraucht viel resourcen.
Ein Timer zu benutzen ist kein guter OOP Stil.
|
Die meisten Ereignisse funtionieren über Nachrichten, das könnte dir also, falls Nachrichten nicht ankommen(was ich für ein Gerücht halte) auch bei einem Click-Ereignis passieren.
Die Komponenten in einem Array zu erfassen, obwohl sie bereits in Components und in Controls verwaltet werden ist auch nicht wirklich "guter" OOP Stil.
Ich persönlich finde den Vorschlag ziemlich gut beim Click-Ereignis die Karten zu löschen, falls du das wirklich willst, wobei ich sie einfach nur unsichtbar machen würde, da dir da eigentlich nicht mehr die Out of Range Exceptions in die Quere kommen können. Und du brauchst keine Umfangreichen Schleifen, um irgendwelche Löschprüfungen zu schreiben.
_________________ ...und da wurde mir klar, dass eine Toolbar keine Kneipe für Heimwerker ist.
|
|
recall
      
Beiträge: 449
|
Verfasst: Mi 24.12.03 12:01
| catweasel hat folgendes geschrieben: | Ein Timer ist nicht zuverlässig.
Ich kann nicht exakt entscheiden was das ontimer ereignis ausgeführt wird . Damit meine ich das Timerereignis selbst.
Ein Timer funktioniert ja über Nachrichten und da kann schon mal eine verlorengehen bzw. sehr spät ankommen.
Ein Timer verbraucht viel resourcen.
|
Also wenn du mit der IDE proggst, dann machst du dir eher weniger Sorgen um die Ressourcen (ist doch geradezu lächerlich: der Anteil des Timers). Das mit den Nachrichten: Naja, wenn eine verlorengeht (was ich noch nie erlebt habe), dann kann doch die nächste (eben 2ms) die Kompo löschen. Du kannst sie ja in der Click-Routine schonmal unsichtbar machen  => Die 1 ms fallen "eher weniger" auf.
Also WENN deine Argumente auch stimmen sollten, dann treffen sie auf die hier vorliegende Situation NICHT zu, denn der Timer soll NUR EINMAL ausgeführt werden und sich danach selbst deaktivieren.
| Zitat: | | Ein Timer zu benutzen ist kein guter OOP Stil. |
BITTE ? Was benutzt du denn sonst so an KOMPONENTEN, die dir einen Timer ersetzen ? (Ich weiss, was man sonst benutzen kann, aber es geht ja um OOP => also welche Kompo ?)
Viele Grüsse.
P.S.: Man muss doch keine pauschales Urteil über Timer fällen. Wenn sie grade gut ins Proggi passen, dann rein damit  .
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Mi 24.12.03 13:18
| recall hat folgendes geschrieben: | | Naja, wenn eine verlorengeht (was ich noch nie erlebt habe), dann kann doch die nächste (eben 2ms) die Kompo löschen. Du kannst sie ja in der Click-Routine schonmal unsichtbar machen :) => Die 1 ms fallen "eher weniger" auf. |
Mit einem Timer schaffst du höchstens 10ms-intervall (unter WinNT usw) und ~40ms (?) unter 9x.
Das fällt zwar auch nicht auf, aber ich halte es trotzdem für keinen guten Stil.
|
|
recall
      
Beiträge: 449
|
Verfasst: Mi 24.12.03 14:17
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| var Form1: TForm1; S: String; I: LongInt;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); begin S := TimeToStr(Now); Repeat Until S<>TimeToStr(Now); S := TimeToStr(Now); Timer1.Enabled := True; end;
procedure TForm1.Timer1Timer(Sender: TObject); begin Inc(I); if S<>TimeToStr(Now) then begin Timer1.Enabled := False; ShowMessage(FloatToStr(1000/I) + ' ms-Interval wird erreicht.'); Form1.Close; end; end;
end. | 16,25ms - Intervall.
Es war mir schon klar, dass ein 1ms-Interval mit einem Timer nicht möglich ist, das war als "ist doch egal wieviele ms er braucht um die Kompo zulöschen"-Aussage gedacht.
@obbschtkuche: Ich verstehe wirklich nicht, wieso du etwas PAUSCHAL gegen Timer hast ? Er ist hier doch wirklich gut geeignet !
Viele Grüsse.
P.S.: Noch eine andere Möglichkeit zum Löschen:
(Die Idee finde ich auch besser als den Timer !)
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
| var Form1: TForm1; dummy: TMemo;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); begin dummy.Text := Button1.Name; end;
procedure TForm1.FormCreate(Sender: TObject); begin dummy := TMemo.Create(Form1); dummy.Visible := False; dummy.Parent := Form1; dummy.OnChange := dummyChange; end;
procedure TForm1.dummyChange(Sender: TObject); begin (Form1.FindComponent(dummy.Lines[0]) as TButton).Free; end;
end. |
WEHE, es sagt einer, das ist kein guter OOP-Stil  .
Viele grüsse.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Mi 24.12.03 14:28
| Zitat: | Mit einem Timer schaffst du höchstens 10ms-intervall (unter WinNT usw) und ~40ms (?) unter 9x.
Das fällt zwar auch nicht auf, aber ich halte es trotzdem für keinen guten Stil. |
@ obschtkuche:
Das sehe ich genauso.
Bei einem Timer wird eine Funktion "Zeit gesteuert ausgeführt, ich möchte es aber an eine Bedingung knüpfen....
| Zitat: | Also WENN deine Argumente auch stimmen sollten, dann treffen sie auf die hier vorliegende Situation NICHT zu, denn der Timer soll NUR EINMAL ausgeführt werden und sich danach selbst deaktivieren.
Also WENN deine Argumente auch stimmen sollten, dann treffen sie auf die hier vorliegende Situation NICHT zu, denn der Timer soll NUR EINMAL ausgeführt werden und sich danach selbst deaktivieren. |
@recall
Wenn er sich deaktivieren soll, wie kann dann "der nächste" x-millisekunden später dann greifen ???
Ein Programm mit Timer ist eben nicht mehr 100% deterministisch aufgebaut. Man kann nicht mehr mit letzter Sicherheit sagen was wann in welcher Reihenfolge ausgeführt wird.
Was passiert z.b. beim unmittelbaren Klick auf die Karte wenn dann noch ein timerevent die Karte löschen will (weil ein paar gefunden wurde)
??
Oder es ird nur ein paar gelöscht weil das Timerereignis zwischen die "Karte-auf-löschen-setzen) Routine fällt.....?
Jeder der schonmal versucht hat eine Uhr per Timer zu basteln wird merken dass sie sehr unrund läuft...
Bis Win98 konnte man ja bei Microsofts MineSweeper auf die Art und Weise bescheissen.... Form verschieben und nicht loslassen.....
Schon ist der Timer Nachrichtentechnisch tot.........
| Zitat: | | BITTE ? Was benutzt du denn sonst so an KOMPONENTEN, die dir einen Timer ersetzen ? (Ich weiss, was man sonst benutzen kann, aber es geht ja um OOP => also welche Kompo ?) |
In solchen Fällen verzichte ich grundsätzlich auf Kompos dieser Art.
Es gibt meist immer eine Alternative zu einem Timer. Es gibt nichts wo es "ohne Timer nicht geht"..
Ahmmm, Warum ich die Karten in einem Array erfasse ?
Ich erspare mit das ganze gefrickel mit FindComponent und dem check auf NIL wenn ich in jede Karte das dazugehörige Bitmap lade etc.....
Es macht schon Sinn jede Karte mit Index ansprechen zu können....
Ausserdem sind die Karten nicht die einigen Controls auf dem Bildschirm..
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|
catweasel 
      
Beiträge: 487
Erhaltene Danke: 1
Win 7 64bit
Delphi 7 Second Sedition V7.2
|
Verfasst: Mi 24.12.03 14:35
| Zitat: | (Form1.FindComponent(dummy.Lines[0]) as TButton).Free;
WEHE, es sagt einer, das ist kein guter OOP-Stil . |
Doooch ich sags ..
Und der Onkel Borland auch
Ein Cast sollte man auch vermeiden wo man kann. Ein cast hat immer das Potential schiefzugehen und soll (weiss nich obs stimmt) sehr "langsam", "resourcenfressend" oder sonstwas sein..
Auf jedenfall unvorteilhaft..
Steht auch so irgendwo in der Delphi Hilfe...
Catweasel
_________________ Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
|
|