Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Kein Komponentenselbstmord bei Delphi ?
catweasel - Di 23.12.03 20:44
Titel: Kein Komponentenselbstmord bei Delphi ?
Hi,
ich habe folgendes Problem:
Ich möchte eine selbst erzeugt Komponente freigeben....
Gut. Ok. .. Das macht man normalerweise mit
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.... :roll:
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
Anonymous - 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 - 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
TomT - 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.
TomT - 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.
catweasel - 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
Anonymous - 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 - 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
Anonymous - 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 - 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 :roll:
Anonymous - 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 - 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....
recall - 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 - 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 :D
Catweasel
TomT - 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.
recall - 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 :D .
Anonymous - 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 - 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 - 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
catweasel - 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 .. :twisted:
Und der Onkel Borland auch :D
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... :roll:
Catweasel
Anonymous - Mi 24.12.03 14:47
Das stimmt nur Teilweise:
Der Cast selbst braucht eigentlich gar keine Zeit; dem Compiler ist es relativ egal als was er jetzt welchen Pointer interpretieren soll ;)
Und wenn man vorher mit if blubb is TButton überprüft ob es sich überhaupt um einen Button handelt, ist der cast auch kein Problem mehr.
TomT - Mi 24.12.03 16:00
was passiert eigentlich in deinem array, wenn du eine Karte löschst? Kann mir das irgendwie nicht in gutem OOP-Stil vorstellen???
recall - Mi 24.12.03 19:58
Du must das nicht mit FindComponent machen !
Ist mir schon klar, dass es daneben gehen kann: Nimm einfache eine globale Variable vom Typ TWinControl und dann mach diese *.Free; in der OnChange-Routine ! Eigentlich gings mir mehr darum die OnChange-Routine zu benutzen !
Viele Grüsse.
catweasel - Mi 24.12.03 22:53
Hi,
hier mal mein onclick przedur...:
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:
| procedure TForm1.bildklick(Sender: TObject); var clickID : integer; x : string; begin zweitwahl := swapbool(zweitwahl);
x := TImage(sender).parent.Parent.name; delete(x,1,1); clickid := strtoint(x); karte[clickid].showcard; if zweitwahl = false then tempid := clickid;
if zweitwahl = true then begin
if karte[clickid].BildID = karte[tempid].BildID then begin showmessage('PAAR GEFUNDEN'); karte[tempid].Free; karte[clickid].Free; exit; end else showmessage('KEIN TREFFER'); karte[clickid].hidecard; karte[tempid].hidecard; end; |
Man kann also ein array auch nur teilweise freigeben, bzw. einzelne Elemente davon..
Swapbool sorgt nur dafür das ein Klick darauf reagiert wenn er eine zweite Karte aufdeckt und eine Paarprüfung stattfindet...
Zweitwahl ist also abwechselnd true oder false....
Und so erzeuge ich meine Karten...:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure TForm1.neumnuClick(Sender: TObject); var x,y,z, paare, reihe : integer; begin paare := creategame; reihe := round(sqrt(paare)); x := 10; y := 10; for z := 1 to paare do begin application.ProcessMessages; karte[z] := TKarte.create(self,x,y,bilderliste.getpfad(z),bilderliste.getid(z),z); karte[z].Parent := self; karte[z].bildx.OnClick := bildklick; inc(x,100); if x> reihe*100 then begin x := 10; inc(y,100); end; end; end; |
der Konstrukto sieht dann so aus...:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| type Tkarte = class(TWincontrol)
private kartex : TPanel; blind : TBitmap; karte : TBitmap; public bildx : TImage; BildID : integer; procedure showcard; procedure hidecard; constructor create(Owner:Tcomponent;xalign,yalign:integer;bildname:string; inhalt,kartenID:integer);reintroduce; destructor destroy;reintroduce;
end; |
und ist so implementiert...
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: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50:
| constructor Tkarte.create(Owner: Tcomponent;xalign,yalign:integer;bildname:string; inhalt,kartenID:integer);
var jbild : TJpegImage; begin inherited create(owner);
width := 100; height := 100; left := xalign; top := yalign;
Name := 'k'+inttostr(kartenID);
blind := TBitmap.Create; blind.LoadFromFile('C:\blind.bmp');
try karte := TBitmap.Create; karte.LoadFromFile(bildname); except jbild := TJpegImage.Create; jbild.LoadFromFile(bildname); karte.Assign(jbild); jbild.Free; end;
kartex := TPanel.Create(self); with kartex do begin Parent := self; Width := 100; Height := 100; Left := 0; top := 0; end;
bildx := TImage.Create(self); with bildx do begin Parent := kartex; Width := kartex.width -10; Height := kartex.Height -10; Left := 5; top := 5; picture.Assign(blind); Stretch := true; bildID := inhalt; end; end; |
Ahja:
Zu der Frage: Wenn Messages verloren gehen........
Wer das noch nie erlebt hat und das mal gerne möchte, der tue folgendes...:
Man nehme ein Memofeld und fülle es mit einer Schleife mit Textzeilen uznd zwar relativ hurtig und OHNE application.processmessages aufzurufen.......
Man stellt fest:
Einige Zeilen werden bei grösseren Texten nicht eingefügt, da die Nachrichten an die Memobox zu schnell kommen um jede davon auch wwirklich abzuarbeiten.......
Oder woran solls sonst liegen ?
Catweasel
Moderiert von
Peter Lustig: Code- durch Delphi-Tags ersetzt
recall - Do 25.12.03 02:10
| Zitat: |
| OHNE application.processmessages aufzurufen....... |
und warum soll man sich absichtlich blöd anstellen ???????
Viele Grüsse.
catweasel - Do 25.12.03 11:01
| Zitat: |
| und warum soll man sich absichtlich blöd anstellen ??????? |
Naja, um zu sehen das es tatsächlich möglich ist dass Nachrichten verloren gehen können, was hier ja allgemein angezweifelt wurde.....
Catweasel
AndyB - Do 25.12.03 12:41
| catweasel hat folgendes geschrieben: |
| Was genau ist da eigentlich das Problem damit wenn eine Komponente sich selbst freigeben will |
Ganz einfach. In der MouseUp Methode wird des OnClick aufgerufen.
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| procedure TControl.MouseUp(...); begin MachWasMitMirSelbst; OnClick(Self); MachNochWasMitMirSelbst; end; |
Wenn du nun Self.Free im OnClick-Ereignis aufrufst, wird die Komponente freigegeben und beim zurückkehren in MouseUp crasht das
MachNochWasMitMirSelbst, da es auf die Komponente zugreift.
| Zitat: |
| und kann man für Komponenten auch ein "release" implementieren wie für Forms, wo ja der MessageQueue vor dem destroy abgearbeitet wird....??? |
Natürlich kann man das.
[quoet="TomT"]Oh, fast vergessen. Die Komponente ruft beim .free alle Destruktoren aller Komponenten auf, von denen deine Komponente erbt.[/quote]
Hää? Eine abgeleitete Klasse ruft zwar die Destruktoren der Vorfahrklassen auf (sofern inherited Destroy im Destruktur aufgerufen wird), aber das bedeutet noch lange nicht, dass wenn ich z.B. einen TButton erzeuge auch noch eine TWinControl, TControl und TComponente Komponente "gratis" dazu bekomme, nur weil TButton von TWinControl erbt.
Oder meinst du mit "erbt", den Owner? Das wäre dann aber auch falsch, denn nur wenn der Owner freigegeben wird, wird die Komponente dann auch freigegeben. Also die andere Richtung.
Bzgl. Timer und Lösch-Flag:
| catweasel hat folgendes geschrieben: |
| Der Punkt ist hier die Performance.... Bei einem MemorySet aus 200 Bildern sind das 400 Karten.... Das dauert schon eine Weile.... |
Soll dein Memory-Spiel auf einem 8086 laufen, damit du den Performance unterschied feststellen kannst?
| Zitat: |
| Ein Timer ist nicht zuverlässig. |
Er ist dahingegend zuverlässig, dass man weiß, dass kein einziges Timer-Ereignis ausgelassen wird. Ob es nun 5 ms zu spät kommt ist eher unwichtig. Windows ist nun mal kein Echtzeit Betriebssystem.
| Zitat: |
| Ich kann nicht exakt entscheiden was das ontimer ereignis ausgeführt wird . Damit meine ich das Timerereignis selbst. |
Ich irgendwie schon. Ein Timer Ereignis wird dann ausgeführt, wenn eine WM_TIMER Message in an der Spitze der Messagequeue steht und dieses wird durch Windows generiert und kommt in mehr oder weniger (je nach Rechenbelastung) regelmäßigen Abständen, dem Interval.
| Zitat: |
| Ein Timer funktioniert ja über Nachrichten und da kann schon mal eine verlorengehen bzw. sehr spät ankommen. |
Das eine Message geschluckt wird, kommt nur vor, wenn die Messagequeue voll ist. Dann werden aber als erstes Benutzer-Interface Ereignisse wie Tastatur und Mouse ausgelassen (wenn ich mich nicht irre). Das ist auch der Grund, warum man mit einem Timer die Messagequeue vollstopfen kann, wenn der Code in OnTimer übermäßig lang braucht oder garnicht terminiert.
| Zitat: |
| Ein Timer verbraucht viel resourcen. |
Ein Timer verbraucht kaum resourcen. 100 Timer verbrauchen viele Resourcen, nämlich 100 mal soviel vie
ein Timer.
| Zitat: |
| Ein Timer zu benutzen ist kein guter OOP Stil. |
Kann dir nicht folgen. Seit wann hat die Programmierart mit dem Programmablauf etwas zu tun?
| 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. |
Wieso hat Microsoft denn überhaupt einen Timer programmiert, wenn er doch gar nicht benutzt werden sollte?
| recall hat folgendes geschrieben: |
Delphi-Quelltext 1: 2: 3:
| S := TimeToStr(Now); Repeat Until S<>TimeToStr(Now); | |
Viel Spaß bei der Endlos Schleife, die neben dem unnötigen TimeToStr auf noch die gesamte CPU mit 100% belastet. Hast du schon einmal gehört, dass man Gleitkommazahlen (was anderes ist TDateTime nicht) auch direkt mit "<" und ">" vergleichen kann?
| catweasel hat folgendes geschrieben: |
| 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. |
Und das kann man, wenn der Benutzer seine Input-Ereignisse (Mouse, Tastatur und abhängige Ereignisse) auslöst? Woher willst du z.B. wissen, dass ein Benutzer nach dem Drücken einer Maustaste nicht vor dem Loslassen eine Taste auf der Tastatur drückt? Ein Programm, dass über Botschaften läuft, kann vom Ablauf her nicht deterministisch sein, wenn ein User vorhanden ist, der über Eingabegeräte verfügt.
| Zitat: |
| 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) |
Gar nichts, weil die Ereignisse der Reihe nach abgearbeitet werden.
| Zitat: |
| Oder es ird nur ein paar gelöscht weil das Timerereignis zwischen die "Karte-auf-löschen-setzen) Routine fällt.....? |
So lange du in "Karte-auf-löschen-setzen" kein Application.ProcessMessages aufrufst, wird das Timer-Ereignis erst bei der Rückkehr in die Messageloop ausgeführt und die ist bestimmt nicht in "Karte-auf-löschen-setzen". Sie ist nämlich außerhalb von jeder Komponente (TApplication mal ausgeschlossen).
| Zitat: |
| Es gibt meist immer eine Alternative zu einem Timer. Es gibt nichts wo es "ohne Timer nicht geht".. |
Natürlich gibt es ab Windows 95 sowas nicht mehr. Das aber nur aus dem einen Grund, da man ab Win95 in der Lage ist, das, was Windows für einen Timer im Hintergrund erledigt, auch selbst nachzuprogrammieren. Womit wir bei Threads angelangt sind. Wenn man aber einen Code im "OnThreadTimer" Ereignis hat, der nicht ohne Synchronize auskommt, hat man genau die gleiche Situation geschaffen, die ein Timer durchläuft. Man hat nämlich dann zwei Möglichkeiten: Synchronize aufrufen oder mit PostMessage den Hauptthread über das Ereignis informieren. Wenn man sich nun die Synchronize Methode genauer anschaut, stellt man fest, dass diese vom Prinzip her genaus so arbeitet wie ein PostMessage.
Bzgl. TTimer und OOP:
| Zitat: |
Doooch ich sags ..
Und der Onkel Borland auch |
Wo schreibt Borland das denn?
| Zitat: |
| Ein cast hat immer das Potential schiefzugehen |
Wenn man nicht aufpasst, schon. Aber als Programmierer sollte man seinen Quellcode kommentieren und auch zu jeder Runtime-Zeit wissen, was in welchen Variablen steht (es ist damit
nicht gemeint: welcher Werte genau in der Variable steht).
| Zitat: |
und soll (weiss nich obs stimmt) sehr "langsam", "resourcenfressend" oder sonstwas sein..
Auf jedenfall unvorteilhaft.. |
Natürlich ist der Typecast unvoreilhaft, wenn mein ein solches Bild von ihm hat. Ein direkter Typecast ohne
as verbraucht nicht einen CPU-Tick, da es eigentlich nichts anderes ist als: "Compiler hör mal zu. Du musst die Variable x nun nicht mehr als TObject betrachten, sondern als TComponent." Es wird kein Maschinencode bei einem Typecast erzeugt.
Nur bei "String to PChar" ist das anders, da Delphi-Strings so handlich sind (im Vergleich zu C/C++ (ohne stl)), nimmt der Compiler ein wenig Programmierleistung ab und fügt bei PChar(string) etwas Compiler-Magic ein.
| Zitat: |
| Steht auch so irgendwo in der Delphi Hilfe... |
Da steht höchstens was über den
as-Operator. Ja, dieser ist etwas langsam, da er einen
is-Aufruf durchführt, der "so lange" braucht. Wenn man aber genau weiß, dass die zu castende Variable einen bestimmten Typ hat, kann man direkt casten ohne das
as. Persönlich verwende ich
as nur, wenn es der Lesbarkeit des Quellcodes dienlich ist.
---------------------------
So viel Falsch-Wissen habe ich schon lange nicht mehr gesehen.
Anonymous - Do 25.12.03 13:13
| AndyB hat folgendes geschrieben: |
Ganz einfach. In der MouseUp Methode wird des OnClick aufgerufen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| procedure TControl.MouseUp(...); begin MachWasMitMirSelbst; OnClick(Self); MachNochWasMitMirSelbst; end; |
Wenn du nun Self.Free im OnClick-Ereignis aufrufst, wird die Komponente freigegeben und beim zurückkehren in MouseUp crasht das MachNochWasMitMirSelbst, da es auf die Komponente zugreift.
|
kleiner Test:
Delphi-Quelltext
1: 2: 3: 4: 5:
| TTest = class i: integer; procedure foo; procedure bar; end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TTest.bar; begin foo; i := 124123; end;
procedure TTest.foo; begin free; end; |
Funktioniert - zumindest bei mir - einwandfrei.
| AndyB hat folgendes geschrieben: |
| Zitat: | | Ich kann nicht exakt entscheiden was das ontimer ereignis ausgeführt wird . Damit meine ich das Timerereignis selbst. |
Ich irgendwie schon. Ein Timer Ereignis wird dann ausgeführt, wenn eine WM_TIMER Message in an der Spitze der Messagequeue steht und dieses wird durch Windows generiert und kommt in mehr oder weniger (je nach Rechenbelastung) regelmäßigen Abständen, dem Interval. |
Und du kannst also auf die Sekunde genau abschätzen, wann diese Nachricht abgearbeitet wird? Ich nicht.
| AndyB hat folgendes geschrieben: |
| 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. |
Wieso hat Microsoft denn überhaupt einen Timer programmiert, wenn er doch gar nicht benutzt werden sollte?
|
Zusammenhang lesen.
| AndyB hat folgendes geschrieben: |
Bzgl. TTimer und OOP:
| Zitat: | Doooch ich sags ..
Und der Onkel Borland auch |
Wo schreibt Borland das denn? |
Zusammenhang lesen.
| AndyB hat folgendes geschrieben: |
| Zitat: | und soll (weiss nich obs stimmt) sehr "langsam", "resourcenfressend" oder sonstwas sein..
Auf jedenfall unvorteilhaft.. |
Natürlich ist der Typecast unvoreilhaft, wenn mein ein solches Bild von ihm hat. Ein direkter Typecast ohne as verbraucht nicht einen CPU-Tick, da es eigentlich nichts anderes ist als: "Compiler hör mal zu. Du musst die Variable x nun nicht mehr als TObject betrachten, sondern als TComponent." |
Schön dass du mich nochmal wiederholst.
| AndyB hat folgendes geschrieben: |
So viel Falsch-Wissen habe ich schon lange nicht mehr gesehen. |
Vielen dank für das nette Gespräch.
AndyB - Do 25.12.03 14:16
| obbschtkuche hat folgendes geschrieben: |
| kleiner Test: |
Netter Test. Dieser hat nur einen Fehler. Er funktiniert nur, da der Delphi-Speichermanager den Speicher nicht sofort freigibt, sondern in eine Free-Memory-Block Liste verschiebt, um später schneller Speicher zu alloziieren. Es kann aber auch passieren, das der Delphi-Speichermanager mehrer zusammenhängende freie Blöcke durch das Free erhält und diese dann an das System zuückgibt, womit ein Zugiff auf die Speicheradresse zu einer Schutzverletzung führt.
Mit diesem Code spielst du somit russisches Roulette.
| Zitat: |
| Funktioniert - zumindest bei mir - einwandfrei. |
Bei mir nur, wenn ich den Delphi-Speichermanager nicht durch einen anderen ersetze. Und auch nur aus zufälligen Gründen, da du zufällig keine zusammenhängenen freien Speicherblöcke erhältst, die der Speichermanager an Windows zurückgeben würde. Das könnte sich aber schnell ändern wenn der Speichermanager mehr zu tun bekommt.
| Zitat: |
| Zitat: | | Ich irgendwie schon. |
Und du kannst also auf die Sekunde genau abschätzen, wann diese Nachricht abgearbeitet wird? Ich nicht. |
Damit meine ich nicht den genauen Zeitpunkt, der in deinem Memory sowieso irrelevant ist. Sondern in welcher Reihenfolge die WM_TIMER Botschaften verarbeitet werden.
| Zitat: |
| Zusammenhang lesen. |
Das habe ich doch. Und für dein Beispiel würde ein Timer sicherlich reichen, auch wenn es eine "elegantere" Methode gibt.
| Zitat: |
| AndyB hat folgendes geschrieben: | Bzgl. TTimer und OOP:
| Zitat: | Doooch ich sags ..
Und der Onkel Borland auch |
Wo schreibt Borland das denn? |
Zusammenhang lesen. |
Was soll ich da nicht gelesen haben? Das TTimer <> OOP ist doch gar nicht so schwer zu lesen.
| Zitat: |
| Schön dass du mich nochmal wiederholst. |
Ich hatte die zuweite Seite nicht gelesen.
| Zitat: |
| Vielen dank für das nette Gespräch. |
Aber immer doch.
Anonymous - Do 25.12.03 15:10
| AndyB hat folgendes geschrieben: |
Netter Test. Dieser hat nur einen Fehler. Er funktiniert nur, da der Delphi-Speichermanager den Speicher nicht sofort freigibt, sondern in eine Free-Memory-Block Liste verschiebt, um später schneller Speicher zu alloziieren. Es kann aber auch passieren, das der Delphi-Speichermanager mehrer zusammenhängende freie Blöcke durch das Free erhält und diese dann an das System zuückgibt, womit ein Zugiff auf die Speicheradresse zu einer Schutzverletzung führt.
Mit diesem Code spielst du somit russisches Roulette. |
Gut, man kann immer noch was dazulernen :)
Allerdings meinte er mit
exakt nicht die Reihenfolge, sondern den Zeitpunkt, und den kann man keineswegs abschätzen.
und es war (soweit ich das gelesen habe) dieser Codeschnipsel gemeint:
Delphi-Quelltext
1:
| (Form1.FindComponent(dummy.Lines[0]) as TButton).Free; |
Ich habe auch keineswegs pauschal etwas gegen Timer, aber sehr wohl wenn sie absolut unnütz und noch dazu kein guter Stil sind.
catweasel - Fr 26.12.03 18:53
Hab mich nu mal ein bischen schlau gemacht.....
Also ich denke ich werde die Variante nehmen das ich gefundene Paare auf hide setze und alle hides bei einem onclick auf eine karte lösche....
Aaaber :
Es funktioniert auch prinzipiell mit dem freigeben "einfach-so), da die Objekte tatsächlich restlos freigegeben werden und nur die AccessViolation Exception Meldung nervt...
TomT hatte geschrieben:
| Zitat: |
| 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). |
Doch das geht..... Habs irgendwo in einem Tut gefunden....
einfach im OnCreate der Hauptform :
Quelltext
1:
| Application.OnExeption := stilljetzt; |
einfügen... Und wenn nun
Quelltext
1: 2: 3: 4:
| procedure stilljetzt(Sender:TObject;E:Exception); begin // end; |
nun nichts anderes macht als garnichts, kann man ALLE Exceptions totschweigen, :D oder eben behandeln... :wink:
Bitte nicht Missverstehen. Programmiertechnisch ist sowas natürlich amoralisch :mahn: :wink:
Catweasel
AndyB - Fr 26.12.03 20:33
Wie wäre es denn hiermit:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| TForm1 = class(TForm) ... private procedure CMRelease(var Message: TMessage); message CM_RELEASE; public procedure ReleaseObject(Obj: TObject); end;
procedure TForm1.CMRelease(var Message: TMessage); message CM_RELEASE; begin if Message.LParam = 0 then inherited else TObject(Message.LParam).Free; end;
procedure TForm1.ReleaseObject(Obj: TObject); begin if Obj <> nil then PostMessage(Handle, WM_RELEASE, 0, Integer(Obj)); end; |
TomT - Fr 26.12.03 21:31
@AndiB
Natürlich werden implizit die Destruktoren aller geerbten Klassen beim zerstören eines Objekts aufgerufen. Die geerbten Objekte gibts nicht dazu, die sind in deinem Objekt inbegriffen und deren Verhalten und Speicherinhalt muss auch wieder freigegeben werden. Daher auch alle Destruktoren. Das ist der Sinn der OOP.
Frohe Weihnachten!
TomT
AndyB - Fr 26.12.03 23:14
| TomT hat folgendes geschrieben: |
| Natürlich werden implizit die Destruktoren aller geerbten Klassen beim zerstören eines Objekts aufgerufen. |
Das klang aber ganz anders mit:
| Zitat: |
| Die Komponente ruft beim .free alle Destruktoren aller Komponenten auf, von denen deine Komponente erbt. |
| Zitat: |
| Die geerbten Objekte gibts nicht dazu |
Das ist mir schon klar. Nur kam es bei deinem ersten Text anders herüber, da du dort von Komponenten schriebst.
TomT - Fr 26.12.03 23:18
Wo ist der Unterschied? Jede Komponente ist eine Klasse. (Womit nicht jede Klasse eine Komponente ist)
recall - Sa 27.12.03 16:02
| Zitat: |
| Viel Spaß bei der Endlos Schleife, die neben dem unnötigen TimeToStr auf noch die gesamte CPU mit 100% belastet. Hast du schon einmal gehört, dass man Gleitkommazahlen (was anderes ist TDateTime nicht) auch direkt mit "<" und ">" vergleichen kann? |
hmm... klar, aber bei TimeToStr geht's erst weiter, wenn EINE GANZE Sekunde vergangen ist (genau das was ich will). Außerdem MENSCH ! Das war doch nur ein kümmerliches Schnelltest-proggi (argh).
OK, soviel zum Beitrag an dem allgemeinen Streit hier.
Andere Möglichkeit zum Komponentenselbstmord: Man erzeugt eine user-defined WM_Message und schreibt ein Application-Ereignis dazu.
Wenn ichs mir Recht überlege : GENAU DAS ist ein Timer !
Also ist ja auch egal wie du das jetzt machst, hauptsache es funzt ;)
Viele Grüsse.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!