![]() |
Delphi-Version: 10.4 Sydney
Interfaces in lokalen Variablen und deren Freigabe
Hallo,
ich habe folgenden Code:
Code:
TMyInterfacedObject fügt die beiden im Konstruktur übergebenen Objekte in eine Liste ein und wenn dann am Ende der Methode das g von Delphi zerstört wird dann zerstört das TMyInterfacedObject automatisch auch die Objekte a und b und ich spare mir das Free am Ende.
procedure TfrmEditBasic.actAddDatapointsExecute(Sender: TObject);
var g:IMyInterfacedObject; a: TMyObject; b: TMyObject1; begin a:=TMyObject.Create; b:=TMyObject1.Create; g:=TMyInterfacedObject.Create(a, b); // liefert ein TMyInterfacedObject zurück // ... end; Mit FastMM4 erhalte ich beim End eine Schutzverletzung. a oder b werden doppelt freigegeben. Wenn ich vor dem End ein g:=Nil mache, dann ist die Schutzverletzung weg. Alternativ habe ich meinen Code wie folgt umgebaut und die lokale Variable weggelassen und dann erhalte ich auch keine Schutzverletzung:
Code:
wobei CreateInterfacdObject wie folgt aussieht:
procedure TfrmEditBasic.actAddDatapointsExecute(Sender: TObject);
var a: TMyObject; b: TMyObject1; begin a:=TMyObject.Create; b:=TMyObject1.Create; CreateInterfacedObject(a,b); //... end;
Code:
Ich verstehe das gerade absolut nicht. Warum funktioniert der ursprüngliche Code nicht oder nur, wenn ich g auf NIL setze? Und warum funktioniert es wenn ich keine lokale Variable verwende?
function CreateInterfacdObject (var v0, v1): IMyInterfacedObject;
begin exit(TMyInterfacedObject.Create(v0,v1)); end; Grüße Stefan |
AW: Interfaces in lokalen Variablen und deren Freigabe
Vielleicht ist es für mich noch zu früh am Morgen, aber wenn du das einkürzen könntest sodass ein lauffähiges Programm überbleibt wäre das super hilfreich.
Ich habe es mal versucht in einem kompletten Programm nachzustellen (so wie du es beschrieben hast) und da ist alles in Ordnung - Da scheint also noch mehr dahinter zu stecken.
Delphi-Quellcode:
Was gibt es denn noch spannendes was bei dir mit
program Project1;
uses FastMM4, System.SysUtils, System.Generics.Collections; type TMyInterfacedObject = class(TInterfacedObject, IInterface) private var objects: TObjectList<TObject>; public constructor Create(const a, b: TObject); destructor Destroy(); override; end; { TMyInterfacedObject } constructor TMyInterfacedObject.Create(const a, b: TObject); begin inherited Create(); objects := TObjectList<TObject>.Create({ownsObjects:}True); objects.Add(a); objects.Add(b); end; destructor TMyInterfacedObject.Destroy(); begin objects.Free(); inherited; end; procedure p(); var a, b: TObject; g: IInterface; begin a := TObject.Create(); b := TObject.Create(); g := TMyInterfacedObject.Create(a, b); end; begin p(); end.
Delphi-Quellcode:
angedeutet ist?
// ...
|
AW: Interfaces in lokalen Variablen und deren Freigabe
Zitat:
Delphi-Quellcode:
FastMM4 sollte dir auch die Stacktraces liefern. Damit kann man die Ursache meistens gut finden.
function CreateInterfacdObject (var v0, v1): IMyInterfacedObject;
begin Result := TMyInterfacedObject.Create(v0, v1); end; |
AW: Interfaces in lokalen Variablen und deren Freigabe
Zu dem reference counting in Interfaces hab ich ein Video im Kanal:
![]() Wie Jänike schreibt mit "Result:=" gibst du Werte zurück. Mit "exit" beendest du die aktuelle Funktion und der aktuell Result-Wert wird genutzt. Das gilt bis Delphi 2009. Danach kann man das Result theoretisch auch im Exit setzen. ![]() Ich kann mir aber vorstellen, dass da vielleicht ein Bug drin ist, welcher die Referenzzählung durcheinander bringt. Persönlich finde die Jänike Methode allerdings schöner als Exit(<Wert>); |
AW: Interfaces in lokalen Variablen und deren Freigabe
Liste der Anhänge anzeigen (Anzahl: 1)
Ein kleiner Test zeigt, dass die Freigabe ordentlich funktioniert:
Delphi-Quellcode:
Object C wurde absichtlich nicht freigegeben:
procedure Test;
var a, b, c: TNameObject; g: IName; begin a := TNameObject.Create('A'); b := TNameObject.Create('B'); c := TNameObject.Create('C'); g := TTestObject.Create('G', a, b); writeln('interface ', g.Name); end; begin try { TODO -oUser -cConsole Main : Code hier einfügen } ReportMemoryLeaksOnShutDown := True; Test; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
Code:
Zeig uns doch mal dein TMyInterfacedObject und was du sonst noch so mit A und B anstellst.
object A create
object B create object C create object G create interface G object A destroy object B destroy object G destroy Unexpected Memory Leak An unexpected memory leak has occurred. The unexpected small block leaks are: 1 - 12 bytes: TNameObject x 1 13 - 20 bytes: UnicodeString x 1 |
AW: Interfaces in lokalen Variablen und deren Freigabe
Zitat:
Der Compiler wiederum könnte zwar theoretisch mit Compilermagic ermitteln, dass dieses Problem an der Stelle besteht, und entsprechend den Referenzzähler korrigieren. Allerdings steht dem das Halteproblem entgegen. Der Compiler kann daher nicht zuverlässig feststellen, ob er an einer Stelle den Referenzzähler korrigieren muss oder nicht. Und deshalb muss man Objektreferenzen stets in einer Variablen speichern um das Problem zu umgehen. Bei Exit besteht das Problem aber nicht. Hier wird IntfCopy aufgerufen und entsprechend der Referenzzähler erhöht. Die bisherigen Angaben reichen daher nicht um das Problem nachzuvollziehen. |
AW: Interfaces in lokalen Variablen und deren Freigabe
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
danke für die vielen Rückmeldungen. Ich konnte das Problem jetzt im Rahmen eines Testprojekt reproduzieren. Das Projekt ist angehängt. Das Problem ist die Verwendung des Objekts in der anonymen Methode in Verbindung mit dem Interface. Da geht was kaputt...
Code:
Jetzt stellt sich die Frage: warum?
unit fMain;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TObjectPtr = ^TObject; IMyInterface = interface(IUnknown) end; TMyInterfacedObject = class(TInterfacedObject, IMyInterface) private var FObjectPtrs: array [0..0] of TObjectPtr; public constructor Create(var aObj: TObject); destructor Destroy; override; end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } procedure TestAnonymProc(aProc: TProc); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} { TMyInterfacedObject } constructor TMyInterfacedObject.Create(var aObj: TObject); begin FObjectPtrs[0] := @TObject( aObj ); TObject( aObj ) := nil; end; destructor TMyInterfacedObject.Destroy; begin if Assigned( FObjectPtrs[0]^) then begin FObjectPtrs[0]^.Free; // <== AV FObjectPtrs[0]^ := nil; end; inherited; end; procedure TForm1.Button1Click(Sender: TObject); var intf: IMyInterface; list: TStringList; begin intf := TMyInterfacedObject.Create( TObject(list) ); list := TStringList.Create; list.Add( 'Item1' ); TestAnonymProc( procedure begin list.Add( 'Item2' ) end ); list.Add( 'Item3' ); end; procedure TForm1.TestAnonymProc(aProc: TProc); begin aProc(); end; end. Grüße Stefan |
AW: Interfaces in lokalen Variablen und deren Freigabe
Warum tust du dir das mit den Zeigern an?
Nimm eine ![]() Siehe mein Beispiel im 2. Beitrag. |
AW: Interfaces in lokalen Variablen und deren Freigabe
Wenn ich die TObjectList nehme löst das nicht das Problem, daß durch die anonyme Methode was kaputt geht....
|
AW: Interfaces in lokalen Variablen und deren Freigabe
Bei mir (aktuelle Community Edition) passiert kein Fehler.
Den Sinn der Pointer sehe ich aber auch nicht. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:04 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