Autor |
Beitrag |
TiRoCkx
      
Beiträge: 16
|
Verfasst: So 24.03.19 21:07
Hallo liebe Delphianer,
ich habe folgendes Problem und folgenden Code:
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:
| procedure TForm2.CreateShapes(n: word; wc: TWinControl); begin SetLength(Shapes, n); Field.Parent := Panel1; while n > 0 do begin Shapes[n] := TShapen.Create(Self); Shapes[n].Parent := wc; case n of 1,2,3,4: Shapes[n].Brush.Color := rgb(255,0,0); 5,6,7,8: Shapes[n].Brush.Color := RGB(255,216,0); 9,10,11,12: Shapes[n].Brush.Color := RGB(58,196,0); 13,14,15,16: Shapes[n].Brush.Color := RGB(0,148,255); end; case Madn.get_tokens of 0: Shapes[n].Shape := stcircle; 1: Shapes[n].Shape := stRoundSquare; end; Shapes[n].Name := 'Shapen'+inttostr(n); Shapes[n].Height := 30; Shapes[n].Width := 30; Shapes[n].Left := UMadn.Start[n].X; Shapes[n].Top := UMadn.Start[n].Y; Shapes[n].Pen.Width := 3; Shapes[n].Visible := true; Shapes[n].Enabled := false; Shapes[n].OnClick := Shapen1Click; n := n-1; end; end; |
So erstelle ich ein dynamisches Array von Shapes (TShapen = TShapes + OnClick Event).
So möchte ich sie zerstören:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm2.destrshapes(); var i: byte; begin for i := 0 to Madn.get_playn*4 do begin FreeAndNil(Shapes[i]); end; end; |
Wenn ich jetzt aber versuche ein neues Spiel zu erstellen mit den Komponenten, die vorher erstellt aber eigentlich auch zerstört wurden, bekomme ich die Fehlermeldung:
"Komponente mit dem Namen Shapen8 ist bereits vorhanden."
Was mache ich beim Zersören falsch?
Ps. ich verwende den Namen um per Sender.Name darauf zugreifen zu können.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 24.03.19 21:24
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
TiRoCkx 
      
Beiträge: 16
|
Verfasst: So 24.03.19 21:38
Na aber ich kann die Objekte nicht noch mal erstellen, weil die Bezeichnung bereits vorhanden seien...
Wie sollte ich es denn dann am besten schreiben?
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 24.03.19 22:15
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Holgerx
      
Beiträge: 59
Erhaltene Danke: 24
Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
|
Verfasst: Mo 25.03.19 06:36
Hmm..
TiRoCkx hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm2.destrshapes(); var i: byte; begin for i := 0 to Madn.get_playn*4 do begin FreeAndNil(Shapes[i]); end; end; | |
Ich denke, da fehlt einfach nur ein
SetLength(Shapes, 0);
Damit das Array nach dem löschen der Shapes auch wieder bereinigt wird..
|
|
icho2099
      
Beiträge: 101
Erhaltene Danke: 12
WIN XP, WIN 7, WIN 10
Delphi 6 Prof, Delphi 2005, FPC
|
Verfasst: Mo 25.03.19 08:30
die Form hält doch immer noch die Referenzen auf die shapes in controls/components weil niemand ihr gesagt hat, daß diese nicht mehr existieren.
|
|
Gausi
      
Beiträge: 8522
Erhaltene Danke: 461
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mo 25.03.19 09:37
Wenn du dich selber um das Freigeben der Komponenten kümmern willst, dann übergebe NIL als Owner im Create der Shapes, also
Delphi-Quelltext 1: 2: 3: 4: 5:
| while n > 0 do begin Shapes[n] := TShapen.Create(Nil); Shapes[n].Parent := wc; |
Dann sollte das Namensproblem eigentlich behoben sein. Aber dann musst du dich wirklich immer um das Aufräumen selbst kümmern.
Edit: Denn dafür ist der Owner ja da. Wenn man bei einer Komponente einen Owner angibt, dann bekommt dieser Owner (Besitzer) die Verantwortung darüber, die Komponente freizugeben, wenn er selbst freigegeben wird (z.B. bei Ende des Programms). Für dynamisch erzeugte und je nach Bedarf erstellte und zerstörte Komponenten ist die Zuweisung an einen Owner nicht sinnvoll. Die Freigabe dieser Komponenten wird dann ja explizit durch die selbst implementierte Programmlogik sichergestellt.
Evtl. geht es auch, den Owner vor dem Freigeben explizit auf Nil zu setzen, das Shape also der Form wegzunehmen. Dann könnte das auch funktionieren, und man muss im Zweifel beim Beenden des Programms nicht selber aufräumen. (Wobei einige auch argumentieren, dass man bei Programmende nicht aufräumen muss - das wäre so, als würde man vor dem Abriss eines Hauses nochmal feucht durchwischen ... aber das ist ein anderes Thema. )
_________________ We are, we were and will not be.
|
|
Sinspin
      
Beiträge: 1292
Erhaltene Danke: 115
Win7
DXE2 Prof, Lazarus
|
Verfasst: Mo 25.03.19 19:28
TiRoCkx hat folgendes geschrieben : | Na aber ich kann die Objekte nicht noch mal erstellen, weil die Bezeichnung bereits vorhanden seien...
Wie sollte ich es denn dann am besten schreiben? |
Das Problem kenne ich. Hat mich schon mehrfach an den Rand zum Wahnsinn getrieben (zwischen Genie und Wahnsinn ist ja bekanntlich nicht viel Platz)
Trotz mehrfach geprüfter Freigabe und Prüfung wie und ob alles ordentlich weg geräumt ist, kann ich den Name nicht wieder vergeben.
Wenn man den Name nicht selber setzt wird er automatisch vergeben. Da Du dir die Shapes ja eh in einem Array merkst brachst du die Namen nicht, also lass es weg sie festzulegen.
Übrigens, ein Array geht wirklich immer von 0 bis Length()-1
Also so:
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:
| procedure TForm2.CreateShapes(n: word; wc: TWinControl); begin SetLength(Shapes, n); Field.Parent := Panel1; Dec(n); while n >= 0 do begin Shapes[n] := TShapen.Create(nil); Shapes[n].Parent := wc; case n of 1,2,3,4: Shapes[n].Brush.Color := rgb(255,0,0); 5,6,7,8: Shapes[n].Brush.Color := RGB(255,216,0); 9,10,11,12: Shapes[n].Brush.Color := RGB(58,196,0); 13,14,15,16: Shapes[n].Brush.Color := RGB(0,148,255); end; case Madn.get_tokens of 0: Shapes[n].Shape := stcircle; 1: Shapes[n].Shape := stRoundSquare; end; Shapes[n].Height := 30; Shapes[n].Width := 30; Shapes[n].Left := UMadn.Start[n].X; Shapes[n].Top := UMadn.Start[n].Y; Shapes[n].Pen.Width := 3; Shapes[n].Visible := true; Shapes[n].Enabled := false; Shapes[n].OnClick := Shapen1Click; Dec(n); end; end; |
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 25.03.19 19:49
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Sinspin
      
Beiträge: 1292
Erhaltene Danke: 115
Win7
DXE2 Prof, Lazarus
|
Verfasst: Mo 25.03.19 20:01
Augen auf, beim Eier kauf: Zeile 5 in meinem Beispiel.
Dann bleibt alles andere wie gehabt.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 25.03.19 20:13
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Sinspin
      
Beiträge: 1292
Erhaltene Danke: 115
Win7
DXE2 Prof, Lazarus
|
Verfasst: Mo 25.03.19 21:34
Probiers einfach aus:
Schalte für den Compiler Bereichsprüfung mit an und kommentier zum testen ob die zündet einmal das erste Dec mit aus.
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:
| procedure TForm1.Button1Click(Sender: TObject); var c, l: integer; test: array of integer; s: string;
begin c := 10; SetLength(test, c); try Dec(c); while c >= 0 do begin test[c] := c*c; Dec(c); end; s := ''; for l := 0 to Length(test)-1 do s := s + IntToStr(test[l])+'~'; MessageDlg(s, mtInformation, [mbOk], 0); finally Finalize(test); end; end; |

_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 25.03.19 21:55
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Sinspin
      
Beiträge: 1292
Erhaltene Danke: 115
Win7
DXE2 Prof, Lazarus
|
Verfasst: Mo 25.03.19 22:34
Probier mein Beispiel doch einfach mal aus. Mein Beispiel, nicht deins! Das ist wesentlich. Denn da ist ein Unterschied!
Wirst sehen, läuft wunderbar.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 25.03.19 22:38
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
Sinspin
      
Beiträge: 1292
Erhaltene Danke: 115
Win7
DXE2 Prof, Lazarus
|
Verfasst: Mo 25.03.19 22:48
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
|
|
Gausi
      
Beiträge: 8522
Erhaltene Danke: 461
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Di 26.03.19 11:44
Frage am Rande ... warum überhaupt die While-Schleife? Das ist doch ein klassischer Fall für for ...
Delphi-Quelltext 1: 2: 3: 4: 5:
| SetLength(Shapes, N); for i := 0 to Length(Shapes)-1 do begin end; |
Und ggf. konzeptionell die Frage, falls es immer noch um Mensch-Ärgere-Dich-Nicht geht: Ist es überhaupt sinnvoll, die Shapes bei einem neuen Spiel neu zu erstellen, oder reicht es nicht auch, die Shapes einmalig beim Programmstart zu erzeugen, und dann nur noch Zuweisungen von einer Spielinstanz auf die Shapes der Form zu setzen? Dann wäre das Namensproblem ja auch umgangen ...
_________________ We are, we were and will not be.
|
|
Holgerx
      
Beiträge: 59
Erhaltene Danke: 24
Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
|
Verfasst: Di 26.03.19 14:57
Hmm..
Um meinen Beitrag ( www.entwickler-ecke....php?p=715327#715327)
nochmal zu wiederholen:
Es fehlt beim Destroy das 'SetLength(Shapes,0)'
Außerdem wurde in der While Schleife der falschen max Index des Array verwendet..
Anbei ein kleines Demo-Tool
(Erstellt mit D6)
Einloggen, um Attachments anzusehen!
|
|
Gausi
      
Beiträge: 8522
Erhaltene Danke: 461
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Di 26.03.19 16:27
Der Fehler mit dem doppelten Namen hat aber nichts mit dem Array zu tun. Der Fehler wird aufgerufen, bzw. die Exception erzeugt, wenn im Create einer Komponente ein Owner angegeben wird, bzw. einer erstellten Komponente ein bereits vorhandener Name zugewiesen wird. Könnte man einem Owner mehrere Komponenten mit dem gleichen Namen übergeben, würde FindComponent(aName) ja nicht mehr laufen. Daher wird das von der VCL überprüft.
Wenn man sich durch den VCL-Code durchklickt, landet man am Ende bei
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure TComponent.ValidateRename(AComponent: TComponent; const CurName, NewName: string); begin if (AComponent <> nil) and not SameText(CurName, NewName) and (AComponent.Owner = Self) and (FindComponent(NewName) <> nil) then raise EComponentError.CreateResFmt(@SDuplicateName, [NewName]); if (csDesigning in ComponentState) and (Owner <> nil) then Owner.ValidateRename(AComponent, CurName, NewName); end; |
Wenn die Komponente mit dem bestimmten Namen noch beim Owner registriert ist, knallt es hier auf die eine oder andere Art. Entweder, weil durch das Shapes[i].Free schon FindComponent ins Leere läuft (müsste dann eine AccessViolation innerhalb FindComponent geben), oder aber weil eine Komponente mit dem Namen bei dem Owner schon registriert ist.
Das hat aber mit den Referenzen in dem eigenen Shape-Array oder gar dessen Länge nichts zu tun. Davon weiß der VCL-Code ja nichts. Wie gesagt: Owner auf Nil setzen, dann ist das mit dem Namensproblem erledigt.
Oder anders: Beim Zerstören wird im Grunde alles richtig gemacht, aber nicht beim Erstellen.
Natürlich kann man die Array-Länge auch auf Null setzen. Wenn man die aber direkt eh wieder erhöht, kann man das auch so lassen, und den Datenmüll darin direkt überschreiben.
_________________ We are, we were and will not be.
|
|
Holgerx
      
Beiträge: 59
Erhaltene Danke: 24
Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
|
Verfasst: Di 26.03.19 17:31
Hmm..
TiRoCkx hat folgendes geschrieben : |
So möchte ich sie zerstören:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm2.destrshapes(); var i: byte; begin for i := 0 to Madn.get_playn*4 do begin FreeAndNil(Shapes[i]); end; end; |
Wenn ich jetzt aber versuche ein neues Spiel zu erstellen mit den Komponenten, die vorher erstellt aber eigentlich auch zerstört wurden, bekomme ich die Fehlermeldung:
"Komponente mit dem Namen Shapen8 ist bereits vorhanden."
|
Ich glaube der Fehler ist banal:
Welchen 'Wert' hat hier 'Madn.get_playn'?
Ich vermute, dass hier die Anzahl der Spieler stehen.
Wenn also beim Neustart 'Shapen9 ist bereits vorhanden' kommt, dann waren beim destrshapes nur 2 Spieler vorhanden (2 * 4 (ohne -1) ergibt dann Shape 0..  und somit wurden die weiteren Shapes nicht gelöscht und es knallt beim Neuanlegen mit dem Namen...
Also Besser 'wirklich' das ganze Array vor Neuanlage löschen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure TForm2.destrshapes(); var i: byte; begin for i := 0 to Length(Shapes)-1 do begin FreeAndNil(Shapes[i]); end; SetLength(Shapes,0); end; |
Das 'SetLength(Shapes,0);' sollte man aber dennoch immer machen, dann knallt es nicht, wenn man anderswo auf ein NIL - Shape versucht zuzugreifen..
Man bekommt dann nur Index out of Bounds und weiß dann wenigsten, wo man suchen muss..
Ach und zum Thema OWNER:
Wenn ein von TComponent abgeleitetes Object mit einem 'Owner' erzeugt wird, dann Signalisiert es diesem Owner, dass es da ist und dieser fügt es seiner Component List hinzu.
Bei FREE (unabhängig wie) wird signalisiert, dass es jetzt weg ist und der Owner entfernt es aus seiner Components.
Wenn nun der Owner gelöscht wird, dann geht er durch seine Component List uns macht alle Objekte.FREE, diese Signalisieren dann, dass sie weg sind und erst dadurch werden diese aus der Liste ausgetragen (nicht vom OWNER selber) 
|
|
|