AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Speicherleck bei der Verwendung von anonymen Methoden

Speicherleck bei der Verwendung von anonymen Methoden

Ein Thema von carlo93 · begonnen am 15. Okt 2011 · letzter Beitrag vom 4. Nov 2011
Antwort Antwort
Thom

Registriert seit: 19. Mai 2006
570 Beiträge
 
Delphi XE3 Professional
 
#1

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 18. Okt 2011, 23:04
Noch eine Merkwürdigkeit im Zusammenhang mit anonymen Methoden:

Eine anonyme Methode ist nach außen hin nur ein einfacher Pointer. Ein SizeOf(TProc) liefert also unter 32 Bit erwartungsgemäß 4 Byte. Im Gegensatz dazu bestehen Objekt-Methoden aus einem Daten- und einem Code-Pointer. Deshalb liefert SizeOf(TMethod)=8 (unter 32 Bit).

Jetzt sollte man natürlich annehmen, daß der doppelt so große Wert von TMethod nicht zuweisungskompatibel zum "kleineren" TProc ist.
Entgegen jeder Logik funktioniert das aber doch: Der Compiler generiert bei einer derartigen Zuweisung einfach eine anonyme Methode und bindet die Variable Self, die zu der Objekt-Methode gehört.
Ich finde dieses Verhalten genial, da damit Ereignishandler wahlweise Objekt- oder anonyme Methoden sein können.

Ein kleines Beispiel:
Delphi-Quellcode:
type
  TNotifyProc = TProc<TObject>; //anstelle von TNotifyEvent

  TMyObject = class
  private
    FValue: Integer;
    FOnChange: TNotifyProc;
    procedure SetValue(Value: Integer);
  public
    property Value: Integer read FValue write SetValue;
    property OnChange: TNotifyProc read FOnChange write FOnChange;
  end;

  TForm1 = class(TForm)
  [...]
  private
    procedure MyObjectChange(Sender: TObject);
  end;

procedure TMyObject.SetValue(Value: Integer);
begin
  if FValue<>Value then
  begin
    FValue:=Value;
    if assigned(FOnChange)
      then FOnChange(Self);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyObject: TMyObject;
begin
  MyObject:=TMyObject.Create;
  try
    MyObject.OnChange:=MyObjectChange;
    MyObject.Value:=1;
    MyObject.OnChange:=procedure(Sender: TObject)
                       begin
                         ShowMessage(IntToStr(TMyObject(Sender).Value));
                       end;
    MyObject.Value:=2;
  finally
    MyObject.Free;
  end;
end;

procedure TForm1.MyObjectChange(Sender: TObject);
begin
  ShowMessage(IntToStr(TMyObject(Sender).Value));
end;
Thomas Nitzschke
Google Maps mit Delphi

Geändert von Thom (18. Okt 2011 um 23:07 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.588 Beiträge
 
Delphi 12 Athens
 
#2

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 18. Okt 2011, 23:48
Referenzen bestehen intern aus einem geheimgehalten Interface.
Und ein Interfacezeiger ist nunmal auch nur ein Pointer.

Schade eigentlich, denn durch die Geheimhaltung und mangels entsprechender öffentlicher Methoden kann man Referenzen dadurch nicht vergleichen, bzw. prüfen was sich darin befindet.


Wobei die Referenzen keine Methoden als anonyme Methoden "binden", sondern sie können einfachen alles "aufnehmen" ... vermutlich besitzen sie intern Speicher (eventuell überladen) und eine Typ-Variable, welche die Art des aufgenomenen "Zeigers" angibt. Bei einem Aufruf wird dann einfach der entsprechende Typ ausgewertet und aufgerufen.


property OnChange: TNotifyProc read FOnChange write FOnChange; .
Hierfür wollte ich mir mal eine TList<> erstellen, welche mehrere Events verwalten kann, aber leider war es mir nicht mehr möglich einmal registrierte Events "geziehlt" wieder aus der Liste zu entfernen.

PS: Im Zusammenhang mit anonymen Methoden, nimmt diese Referenz auch noch ganze Sätze von geshareten Variablenzeigern mit in sich auf.

Delphi-Quellcode:
    MyObject.OnChange:=procedure(MySender: TObject)
                       begin
                         ShowMessage(MySender.ClassName + ' ' + IntToStr(TMyObject(MySender).Value) + ' ' + Sender.ClassName);
                       end;
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (18. Okt 2011 um 23:59 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.052 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#3

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 07:16
Referenzen bestehen intern aus einem geheimgehalten Interface.
Und ein Interfacezeiger ist nunmal auch nur ein Pointer.

Schade eigentlich, denn durch die Geheimhaltung und mangels entsprechender öffentlicher Methoden kann man Referenzen dadurch nicht vergleichen, bzw. prüfen was sich darin befindet.
Anonyme Methoden sind ein Interface mit einer Invoke Methode, welche die passenden Parameter hat.
Das kann man testen, indem man einfach das hier schreibt
Delphi-Quellcode:
type
  TProcObject = class(TInterfacedObject, TProc)
  public
    procedure Invoke;
  end;
Weiterhin kann man über RTTI auf das Object, was dahinter steckt, zugreifen (siehe dieser Artikel).

Vergleichen und in einer Liste speichern müsste somit auch kein Problem sein. Man kann auch andersrum eine anonyme Methode an ein Event hängen (man muss sich nur klar machen, dass ein Event kein managed type ist, dementsprechend muss man sich da dann selber drum kümmern) Wie das geht, hat Barry in diesem Artikel beschrieben.

Mit diesem Wissen kann man eigentlich fast alles mit einer anonymen Methode machen.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.588 Beiträge
 
Delphi 12 Athens
 
#4

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 09:39
Delphi-Quellcode:
unit Unit1;

interface

uses
  SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, Generics.Defaults, Generics.Collections, StdCtrls;

type
  TCallback = TProc<TObject>;
  TForm1 = class(TForm)
    RegisterButton: TButton;
    UnregisterButton: TButton;
    CallButton: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure RegisterButtonClick(Sender: TObject);
    procedure UnregisterButtonClick(Sender: TObject);
    procedure CallButtonClick(Sender: TObject);
    procedure MyCallback(Sender: TObject);
  private
    FList: TList<TCallback>;
    procedure RegisterCallback(CB: TCallback);
    procedure UnregisterCallback(CB: TCallback);
    procedure DoCallbacks(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FList := TList<TCallback>.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FList.Free;
end;

procedure TForm1.RegisterButtonClick(Sender: TObject);
begin
  RegisterCallback(MyCallback);
end;

procedure TForm1.UnregisterButtonClick(Sender: TObject);
begin
  UnregisterCallback(MyCallback);
end;

procedure TForm1.CallButtonClick(Sender: TObject);
begin
  DoCallbacks(Self);
end;

procedure TForm1.MyCallback(Sender: TObject);
begin
  ShowMessage('Hallo');
end;

procedure TForm1.RegisterCallback(CB: TCallback);
begin
  if FList.IndexOf(CB) < 0 then
    FList.Add(CB);
end;

procedure TForm1.UnregisterCallback(CB: TCallback);
begin
  //if FList.IndexOf(CB) >= 0 then
  // ShowMessage('ist drin');
  FList.Remove(CB);
end;

procedure TForm1.DoCallbacks(Sender: TObject);
var
  i: Integer;
begin
  for i := FList.Count - 1 downto 0 do
    FList[i](Sender);
end;

end.
Du kannst UnregisterCallback so oft aufrufen, wie du willst.
Bei jedem Aufruf wird ein neues Interface aus Callback erstellt.
Also kann dieser Callback nicht mehr in der Liste gefunden werden.


Aber danke für den Link, womöglich läßt sich das ja im einer schönen Vergleichsfunktion verbauen.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.052 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#5

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 12:36
Aber danke für den Link, womöglich läßt sich das ja im einer schönen Vergleichsfunktion verbauen.
Leider nicht (zumindest nicht mit dem mir verfügbaren Wissen aus den beiden genannten Blogs). Ich weiß keine Möglichkeit, an den Code Pointer der gewrappten Methode zu kommen. Ich hab es geschafft an das gecapturte Self Feld und über die interface VMT an die Invoke Methode zu kommen. Diese ist aber unterschiedlich zwischen RegisterCallback und UnregisterCallback. Das einzige, was ich darüber verhindern konnte, war, dass man mehrfach die gleiche Methode registriert.

In dieser Hinsicht haben wir also dasselbe Problem, wie in C#. Dort wird als Lösung eine Variable vorgeschlagen. Wenn du dir also eine Feldvariable vom Typ TCallback machst und dort dein MyCallback zuweist, kannst du im weiteren Verlauf mit dieser Variable arbeiten und das von dir erwartete Verhalten bekommen (dass z.b. TList.Contains oder IndexOf funktioniert).
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.588 Beiträge
 
Delphi 12 Athens
 
#6

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 14:09


Tja, wäre die Interfacedeklration öffentlich, und hätte Emba entsprechende (Vergleichs)Methoden verbaut,
oder hätte man den Vergleichsoperator entsprechend ausgelegt, dann gäbe es das Problem nicht.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.052 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#7

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 16:05


Tja, wäre die Interfacedeklration öffentlich, und hätte Emba entsprechende (Vergleichs)Methoden verbaut,
oder hätte man den Vergleichsoperator entsprechend ausgelegt, dann gäbe es das Problem nicht.
Wenn du $M+ setzt, kannst du dir die RTTI der anonymen Methode anschauen. Hilft dir nur nicht weiter bei dem Problem mit der gewrappten regulären Methode.
Ich werd mal Barry fragen, ob es eine Möglichkeit gibt, diese herauszufinden. Das ist atm das Wissen, was mit noch fehlt, um eine Vergleichsroutine zu bauen.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:18 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz