Autor Beitrag
TiRoCkx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 16



BeitragVerfasst: So 24.03.19 20:07 
Hallo liebe Delphianer,

ich habe folgendes Problem und folgenden Code:

ausblenden 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:

ausblenden 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



BeitragVerfasst: So 24.03.19 20:24 
- Nachträglich durch die Entwickler-Ecke gelöscht -
TiRoCkx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 16



BeitragVerfasst: So 24.03.19 20: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



BeitragVerfasst: So 24.03.19 21:15 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 58
Erhaltene Danke: 23

Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
BeitragVerfasst: Mo 25.03.19 05:36 
Hmm..

user profile iconTiRoCkx hat folgendes geschrieben Zum zitierten Posting springen:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 83
Erhaltene Danke: 9

WIN XP, WIN 7, WIN 10
Delphi 6 Prof, Delphi 2005
BeitragVerfasst: Mo 25.03.19 07: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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8432
Erhaltene Danke: 439

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.2 CE
BeitragVerfasst: Mo 25.03.19 08:37 
Wenn du dich selber um das Freigeben der Komponenten kümmern willst, dann übergebe NIL als Owner im Create der Shapes, also
ausblenden 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. :) )

_________________
Oel ngati kameie.
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1193
Erhaltene Danke: 100

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Mo 25.03.19 18:28 
user profile iconTiRoCkx hat folgendes geschrieben Zum zitierten Posting springen:
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:

ausblenden volle Höhe 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:
procedure TForm2.CreateShapes(n: word; wc: TWinControl);
begin
  SetLength(Shapes, n); // indexwerte sind dann 0..n-1
  Field.Parent := Panel1;
  Dec(n); // compiler magic für n := n-1
  while n >= 0 do
  begin
    Shapes[n] := TShapen.Create(nil); // du kümmerst dich ums freigeben, also hier 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].Name := 'Shapen'+inttostr(n); <-- weglassen
    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); //n := n-1;
  end;
end;

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.03.19 18:49 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1193
Erhaltene Danke: 100

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Mo 25.03.19 19:01 
Augen auf, beim Eier kauf: Zeile 5 in meinem Beispiel.
Dann bleibt alles andere wie gehabt.

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.03.19 19:13 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1193
Erhaltene Danke: 100

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Mo 25.03.19 20: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.
ausblenden 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;

:rofl:

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.03.19 20:55 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1193
Erhaltene Danke: 100

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Mo 25.03.19 21: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.

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.03.19 21:38 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1193
Erhaltene Danke: 100

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Mo 25.03.19 21:48 
Der "Trick" ist Word als Schleifenvariable. Mit Word knallts, mit Integer nicht.
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Mag sein, nur geht es hier um das Erzeugen und Freigeben von Shapes. :zwinker:

Das ist jetzt aber gerade noch rechtzeitig aufgefallen :wink:
Narses macht gleich Hundefutter aus uns wenn wir nicht aufhören

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8432
Erhaltene Danke: 439

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.2 CE
BeitragVerfasst: Di 26.03.19 10:44 
Frage am Rande ... warum überhaupt die While-Schleife? Das ist doch ein klassischer Fall für for ... :gruebel:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
SetLength(Shapes, N);
for i := 0 to Length(Shapes)-1 do
begin
  // do something with Shapes[i]
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 ...

_________________
Oel ngati kameie.
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 58
Erhaltene Danke: 23

Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
BeitragVerfasst: Di 26.03.19 13: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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8432
Erhaltene Danke: 439

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.2 CE
BeitragVerfasst: Di 26.03.19 15: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

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TComponent.ValidateRename(AComponent: TComponent;
  const CurName, NewName: string);
begin
  if (AComponent <> niland not SameText(CurName, NewName) and
    (AComponent.Owner = Self) and (FindComponent(NewName) <> nilthen
    raise EComponentError.CreateResFmt(@SDuplicateName, [NewName]);
  if (csDesigning in ComponentState) and (Owner <> nilthen
    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.

_________________
Oel ngati kameie.
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 58
Erhaltene Danke: 23

Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
BeitragVerfasst: Di 26.03.19 16:31 
Hmm..


user profile iconTiRoCkx hat folgendes geschrieben Zum zitierten Posting springen:

So möchte ich sie zerstören:

ausblenden 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..8) 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:

ausblenden 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) ;)