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
Thema durchsuchen
Ansicht
Themen-Optionen

Speicherleck bei der Verwendung von anonymen Methoden

Ein Thema von carlo93 · begonnen am 15. Okt 2011 · letzter Beitrag vom 4. Nov 2011
Antwort Antwort
Seite 3 von 3     123   
Benutzerbild von Stevie
Stevie

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

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 08: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.184 Beiträge
 
Delphi 12 Athens
 
#22

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 10: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.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

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

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 13: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.184 Beiträge
 
Delphi 12 Athens
 
#24

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 15: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.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

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

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 19. Okt 2011, 17: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
Benutzerbild von Stevie
Stevie

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

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 20. Okt 2011, 09:04
Antwort von Barry:
Zitat:
When you assign a regular method to a method reference it is actually identical to writing an anonymous method that wraps the method call:

Delphi-Quellcode:
TFooType = reference to procedure(fooArg: TFooArg);
foo: TFooType;

foo := Self.Bar;

foo := procedure(fooArg: TFooArg)
  begin
    Self.Bar(fooArg);
  end;
This captures 'Self' - so you could theoretically find out the instance to which the method reference is referring to, using RTTI tricks - but you would need to disassemble the code of the anonymous method to get to the method itself. If the method is virtual or dynamic, it will be using a different calling mechanism than if it is non-virtual, and similarly it will be more or less obscured.
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.184 Beiträge
 
Delphi 12 Athens
 
#27

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 4. Nov 2011, 08:23
Nee, es geht einfach darum rauszufinden was darin ist ... man kann schlecht prüfen ob es sich um eine Referenz auf eine Prozedur, auf eine Methode, eine Klassenmethode oder eine anonyme Methode handelt und wenn, auf welche gezeigt wird
und dementsprechend kann man diese referenzen auch nicht direkt vergleichen. (außer man hat zwei gleiche Referenzen, aber eben nicht "zeigt Referenz auf meine Methode?")

Schade ist aber, daß
- dieses Interface wirklich nur 4 Funktionen, bzw. eine Funktion exportiert ... die Call-Methode, _AddRef, _Release, QueryInterface
(hätte ja sein können, daß es da noch geheime "Vergleichs-/Prüffunktionen" gibt)
- daß zu dem Interface zwar eine TypeInfo zu finden ist, aber dort nur deklariert ist, daß es keine näheren Infos (RTTI) zu den Methoden gibt, also weder Name noch Parameterliste (aber das läßt sich halbwegs leicht rausfinden)
Delphi-Quellcode:
methCnt := ReadU16(P); // 4 = 3 des Vorfahren + 1 Eigene
hasRtti := ReadU16(P); // $FFFF = hab nix weiter
- und das Objekt innerhalb dieses Interfaces besitzt nur ein Feld, das ebefalls nicht in der RTTI aufgelistet wird und welches (vermutlich) ein Pointer ist ... aber was genau in dem Pointer liegt, bleibt ebenfalls geheim und man kann nur Vermutungen anstellen.
Genauere Infos wären schon schön gewesen.
- Zu dem Objekt in dem Interface gibt es zwar eine kleine RTTI, aber dort fehlen viele Dinge, das Feld wird nicht erwähnt und auch nicht die Call-Methode, bzw. ob es weitere Methoden gibt.

Nja, von den Call-Methoden gibt es drei Arten ... da müßte man jetzt analysieren was die machen.
Darüber hatte wohl jemand schonmal rausgefunden, wie man den Methodenzeiger wieder extrahiert (wenn man genau weiß, daß eine Methode ist in der Referenz steckt).
aus'm anderen Thread hier herverschoben
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

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

AW: Speicherleck bei der Verwendung von anonymen Methoden

  Alt 4. Nov 2011, 11:15
Wenn du {$M+} über die Definition der anonymen Methode schreibst, bekommst du RTTI für diesen Typ. Wie ja schon gesagt, sind anonyme Methoden nix anderes als Interfaces.

Mit folgender Routine kannst du dir die Signatur der Invoke Methode und die Felder für die captured Variablen ausgeben lassen:

Delphi-Quellcode:
procedure ListProcedureRefRtti(var MethodRef;
  const TypeInfo: Pointer; const Lines: TStrings);
var
  i: IInterface absolute MethodRef;
  o: TObject;

  c: TRttiContext;
  t: TRttiType;
  f: TRttiField;
  m: TRttiMethod;
begin
  o := i as TObject;

  t := c.GetType(TypeInfo);
  Lines.Add('Interface: ' + t.ToString);
  Lines.Add('Method:');

  for m in t.GetMethods do
  begin
    if m.Parent = t then
      Lines.Add(m.ToString);
  end;

  t := c.GetType(o.ClassInfo);
  Lines.Add('Object: ' + t.ToString);
  Lines.Add('Fields:');

  for f in t.GetFields do
  begin
    if f.Parent = t then
      Lines.Add(f.ToString);
  end;
end;
Bedenke dabei, dass im Falle von mehreren Anonymen Methoden im gleichen Scope nur 1 Objekt erzeugt wird.

Daher hast du bei folgendem Aufruf jedesmal Self und i als Felder. Ich habe hierzu TProc und TFunc lokal redefiniert und ihnen das {$M+} verpasst. Ich hatte Barry auch schon vor einiger Zeit darauf angesprochen das eventuell mal in die SysUtils Definitionen aufzunehmen:

Delphi-Quellcode:
var
  p: TProc;
  f: TFunc<Integer, Boolean>;
  i: Integer;
begin
  Memo1.Clear;

  p := procedure begin ShowMessage(Caption) end;
  ListProcedureRefRtti(p, TypeInfo(TProc), Memo1.Lines);

  f := function(Arg: Integer): Boolean begin Result := Arg mod 2 = 0 end;
  ListProcedureRefRtti(f, TypeInfo(TFunc<Integer, Boolean>), Memo1.Lines);

  p := procedure begin ShowMessage(IntToStr(i)) end;
  ListProcedureRefRtti(p, TypeInfo(TProc), Memo1.Lines);
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   


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 20:32 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