Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Events und SpeicherLeaks bei dynamischen Buttons (https://www.delphipraxis.net/185873-events-und-speicherleaks-bei-dynamischen-buttons.html)

Alexander I 14. Jul 2015 10:50

Events und SpeicherLeaks bei dynamischen Buttons
 
Meine Fragestellung:

Es gibt eine Form1. Diese enthält drei Buttons, ShowForm2 und ShowForm3 und Close.
Es gibt eine Form2. Diese enthält zwei zur Laufzeit mittels TButtonNames erzeugt Buttons Close und ShowForm3.
Es gibt eine Form3. Diese enthält einen zur Laufzeit mittels TButtonNames erzeugten Button Close.

In der Projekt.dpr wird zum Schluss nach Speicherlöchern abgefragt, mit: ReportMemoryLeaksOnShutdown := true;

Es gibt eine Klasse TButtonNames .
Delphi-Quellcode:

type
  TButtonNames = class
  public
   procedure LinkProcedure1(linkedProcedure: TLinkedProcedure);
   function ButtonsForForm2(anzahlButton: integer): TArrayOfTBitBtn;
   function ButtonsForForm3(anzahlButton: integer): TArrayOfTBitBtn;
   destructor destroy; override;
  end;
implementation

var
  handlerKlickEvent: THandlerLinkedProcedure;

destructor TButtonNames.destroy;
begin
  FreeAndNil(handlerKlickEvent);
  for i := 0 to High(fbuttons) do
    FreeAndNil(fbuttons[i]);
  inherited;
end;
procedure TButtonNames.LinkProcedure1(linkedProcedure: TLinkedProcedure);
begin
  handlerKlickEvent := THandlerLinkedProcedure.Create;
  handlerKlickEvent.OnKlick := linkedProcedure;
end;

function TButtonNames.ButtonsForForm2(anzahlButton: integer): TArrayOfTBitBtn;
begin
  for i := 0 to anzahlButton - 1 do
  begin
    fbuttons[i] := TBitBtn.Create(form);
    fbuttons[i].OnClick := handlerKlickEvent.GetKlick;
  end;
end;
function TButtonNames.ButtonsForForm3(anzahlButton: integer): TArrayOfTBitBtn;
begin
  for i := 0 to anzahlButton - 1 do
  begin
    fbuttons[i] := TBitBtn.Create(form);
    fbuttons[i].OnClick := handlerKlickEvent.GetKlick;
   end;
end;
Das Event:
Delphi-Quellcode:
  TLinkedProcedure = procedure(sender: TObject) of object;
  THandlerLinkedProcedure = class
  private
    fLinkedProcedure: TLinkedProcedure;
  public
    property OnKlick: TLinkedProcedure read fLinkedProcedure write fLinkedProcedure;
    procedure GetKlick(sender: TObject);
  end;
Form2 und Form3 erzeugen bei Aufruf die Buttons.
In Form2:
Delphi-Quellcode:
Buttons := TButtonNames.Create;
Buttons.LinkProcedure1(EmpfangeKlick);
Buttons.ButtonsForForm2(2); // oder NamesForForm3(1);
in Form3:
Delphi-Quellcode:
Buttons := TButtonNames.Create;
Buttons.LinkProcedure1(EmpfangeKlick);
Buttons.ButtonsForForm3(1);
beide haben LinkProcedure1(EmpfangeKlick);

EmpfangeKlick sind Prozeduren in Form2 und Form3:
Delphi-Quellcode:
procedure Form2.EmpfangeKlick(Sender: TObject);
begin
  if (Sender = fbuttons[0]) then
    Close;
  if (Sender = fbuttons[1]) then
    ShowForm3;
end;
Wenn jetzt in Form1 auf Button ShowForm3 geklickt wird, erscheint Form3 mit allen erzeugten Buttons, Nämlich der Button „Close“.

Form3 wird durch klick auf „Close“ geschlossen. Form1 mit Klick auf Close geschlossen – Keine SpeicherLeaks werden angezeigt.

Wenn in Form1 auf ShowForm2 geklickt wird, erscheint Form2 mit allen erzeugten Buttons. Nämlich ShowForm3 und Close.
Form2 schließen mit Close. Form1 schließen. - Keine SpeicherLeaks werden angezeigt.

Bei Klick in Form2 auf ShowForm3 wird Form3 angezeigt mit dem Close Button.
Schließen Form3 mit Close, gehts zurück zu Form2. Form2 schließen mit Close. Form1 schließen und
SpeicherLeaks werden angezeigt.


Wenn in TButtonNames eine zweite Variable
Delphi-Quellcode:
handlerKlickEvent2: THandlerLinkedProcedure;
erzeugt wird, und eine zweite Prozedure
Delphi-Quellcode:
procedure TButtonNames.LinkProcedure2(EmpfangeKlick);
begin
  handlerKlickEvent2 := THandlerLinkedProcedure.Create;
  handlerKlickEvent2.OnKlick := linkedProcedure;
end;
und aufruf in Form3:
Delphi-Quellcode:
Buttons := TButtonNames.Create;

Buttons.LinkProcedure2(EmpfangeKlick);

Buttons.ButtonsForForm3(1);
Dann werden niemals SpeicherLeaks angezeigt.

Warum muß ich eine zweite Variable und eine zweite procedure in TErzeugeButtons erstellen?
Oder, was was hab ich falsch gemacht?

Danke im voraus.

CarlAshnikov 14. Jul 2015 11:38

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
Hi,

wäre interessant zu wissen wo Speicherleaks angezeigt werden.

Das Problem liegt meiner Meinung nach daran, dass du nur eine Variable hast:
Delphi-Quellcode:
var handlerKlickEvent: THandlerLinkedProcedure;
.


Die Referenz wird bei

Delphi-Quellcode:
 procedure TButtonNames.LinkProcedure1(linkedProcedure: TLinkedProcedure);
begin
  handlerKlickEvent := THandlerLinkedProcedure.Create;
  handlerKlickEvent.OnKlick := linkedProcedure;
end;
einfach überschrieben wenn du es 2 mal aufrufst und kannst du sie nicht mehr freigeben.

frankyboy1974 14. Jul 2015 11:59

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
Hallo,

der Quellcode kommt mir etwas seltsam vor

Delphi-Quellcode:
function TButtonNames.ButtonsForForm2(anzahlButton: integer): TArrayOfTBitBtn;
begin
  for i := 0 to anzahlButton - 1 do
  begin
    fbuttons[i] := TBitBtn.Create(form);
    fbuttons[i].OnClick := handlerKlickEvent.GetKlick;
end;
function TButtonNames.ButtonsForForm3(anzahlButton: integer): TArrayOfTBitBtn;
begin
  for i := 0 to anzahlButton - 1 do
  begin
    fbuttons[i] := TBitBtn.Create(form);
    fbuttons[i].OnClick := handlerKlickEvent.GetKlick;
end;
Ohne es ausprobiert zu haben, fehlt hier nicht ein (zwei) End;

mfg

frank

Alexander I 14. Jul 2015 12:17

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
@CarlAshnikov

SpeicherLeaks werden beim schließen des Programms angezeigt.



Es werden zwei verschiedene Objekte von TButtonNames erzeugt. Einmal in Form2 und einmal in Form3. Jede Form hat ihre eigenen Buttons.
Die Button von Form3 sollten also die von Form2 nicht kennen.
D.h. Button von Form3 haben ihre eigene LinkProcedure1 und die von Form2 ebenfalls ihre eigene LinkProcedure1 und dazu ihre eigene handlerKlickEvent Variable.


Sonst wäre das doch so, als wenn ich zwei Objekte vom Typ TAuto erstelle und die teilen sich ein Rad.
wenn ich

Delphi-Quellcode:
implementation
var
rad1,rad2,rad3,rad4 : TRad
mache.

Oder?

CarlAshnikov 14. Jul 2015 12:34

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
Doch genau das tun sie, wenn du es so definierst. Wenn etwas nur zu einem Objekt gehören soll dann muss du das als Feld des Objektes definieren:
Delphi-Quellcode:
TAuto = class
private
  fRad1,
  fRad2,
  fRad3,
  fRad4: TRad;
public
  procedure Fahren;
end;
Ist in diesem Fall aber egal weil du bei jedem Aufruf der
Delphi-Quellcode:
procedure TButtonNames.LinkProcedure1(linkedProcedure: TLinkedProcedure);
immer wieder über deine Referenz drüberbügelst. Dass du die Variablen unterhalb von
Delphi-Quellcode:
implementation
definierst sorgt nur dafür, dass außerhalb der Unit keiner darauf zugreifen kann.

Kleiner Test gefällig?

Delphi-Quellcode:
 
  TTest = class
  public
    procedure Test;
  end;

  TForm2 = class(TForm)
    procedure FormCreate(Sender: TObject);
  end;


implementation

var
  globalObj: TObject;

procedure TForm2.FormCreate(Sender: TObject);
var
  vT1, vT2: TTest;
begin
  vT1 := TTest.Create;
  vT2 := TTest.Create;

  vT1.Test;
  vT2.Test;
end;

{ TTest }

procedure TTest.Test;
begin
  if assigned(globalObj) then
    raise Exception.Create('Oha')
  else
    globalObj := TObject.Create;
end;

Zoot 14. Jul 2015 12:43

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
Zitat:

Zitat von Alexander I (Beitrag 1308635)
Meine Fragestellung:

Es gibt eine Form1. Diese enthält drei Buttons, ShowForm2 und ShowForm3 und Close.
Es gibt eine Form2. Diese enthält zwei zur Laufzeit mittels TButtonNames erzeugt Buttons Close und ShowForm3.
Es gibt eine Form3. Diese enthält einen zur Laufzeit mittels TButtonNames erzeugten Button Close.

In der Projekt.dpr wird zum Schluss nach Speicherlöchern abgefragt, mit: ReportMemoryLeaksOnShutdown := true;

Es gibt eine Klasse TButtonNames .

Wo ist denn dein fButtons deklariert?
Und wo wird die Größe des Arrays gesetzt?
Ich sehe nirgends ein SetLength?

Alexander I 14. Jul 2015 12:46

AW: Events und SpeicherLeaks bei dynamischen Buttons
 
Ah, natürlich.

Alles klar jetzt.

Der
Delphi-Quellcode:
handlerKlickEvent: THandlerLinkedProcedure;
muß zu den Objekt vars.

Vielen danke für die Erleuchtung.


Gruß


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz