![]() |
Meine neuste ARC-Hölle
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Zusammen!
Wenn man ein Interface erzeugt und es wieder auf NIL setzt, was erwartet man dann? Richtig, dass der Destructor des Objects aufgerufen wird... Auf Windows haben wir kein ARC, also was macht der gute App-Programmierer... Richtig keine Object verwenden, sondern unter allen Plattformen nur InterfacedObjects verwenden - das sollte ja überall gleich funktionieren... Dachte ich... Anbei ein kleines Testprogramm zum gruseln... Mavarik |
AW: Meine neuste ARC-Hölle
Mit einem Beitrag wie diesem erzeugst Du eigentlich nur Unsicherheit und Halbwissen auf allen Seiten. Hast Du analysiert, an welcher Stelle das von Dir beobachtete Verhalten vom erwarteten Verhalten abweicht? Oder ist es genau das, was Du im Rahmen dieser Diskussion herausfinden möchtest?
|
AW: Meine neuste ARC-Hölle
Zitat:
In dem Fall Button1 vermute ich, du spielst auf die verspätete Freigabe an, die erst beim Beenden der Routine erfolgt. Hier kommen drei Dinge zusammen, die zu diesem Verhalten führen:
In Punkt 2 erzeugt der Compiler in dieser Konstellation eine versteckte Variable für den Rückgabewert und weist diesen getrennt der globalen Variablen zu. Diese versteckte Variable wird aber eben erst bei Verlassen des Scope auf nil gesetzt. Was Button2 betrifft, kann ich keine Besonderheit erkennen. Was erwartest du denn dort und was passiert bei dir? |
AW: Meine neuste ARC-Hölle
Zirkuläre Referenz - der Aufruf von FInvoke in der anonymen Methode in TFDKMessageHandler captured Self implizit und sorgt unter ARC somit für ein __ObjAddRef -> memory leak
Ist schon länger bekannt, dass anonyme Methoden unter ARC schnell mal Leaks erzeugen - ![]() Was wir in Spring4D typischerweise an solchen Stellen machen, ist folgendes:
Delphi-Quellcode:
constructor TFDKMessageHandler<TMSG>.Create(AInvoke: TMyMSGType);
{$IFDEF AUTOREFCOUNT} var capturedSelf: Pointer; // alternativ mit [unsafe] dann spart man sich den hardcast weiter unten, aber da gabs in früheren Versionen noch Probleme, daher stumpf mit Pointer {$ENDIF} begin inherited Create; FInvoke := AInvoke; {$IFDEF AUTOREFCOUNT} capturedSelf := Self; {$ENDIF} FID := TMessageManager.DefaultManager.SubscribeToMessage(TFDKMSGType<TMSG>,Procedure (Const Sender : TObject;Const M : TMessage) begin {$IFDEF AUTOREFCOUNT}with TFDKMessageHandler<TMSG>(capturedSelf) do{$ENDIF} FInvoke(Sender,TMSG(TFDKMSGType<TMSG>(M).Value)); end); end; |
AW: Meine neuste ARC-Hölle
Zitat:
|
AW: Meine neuste ARC-Hölle
Zitat:
|
AW: Meine neuste ARC-Hölle
Zitat:
|
AW: Meine neuste ARC-Hölle
Zitat:
Hierbei gehe ich immer erstmal davon aus: 1. Der Fehler liegt bei mir 2. Der Fehler liegt an der aktuellen Delphi-Version 3. Es ist ein genereller Fehler Und dann stellt sich immer die Frage QC oder nicht... Daher tausche ich mich "gerne" vorher mit dem ein oder anderen aus. :stupid: Zitat:
Zitat:
Mein kleines Testprogramm - abgesehen von den Erklärungen von Uwe und Stefan, die natürlich richtig sind - zeigt aber sicherlich auf, dass sich das Verhalten unter ARC nicht immer so offensichtlich zeigt, wir man es erwartet. Mavarik |
AW: Meine neuste ARC-Hölle
Und jetzt versuch mal so ein DOM hinzubekommen, dass unter ARC und NichtARC funktioniert und wo es massig Kreisreferenzen gibt, ohne ein Speicherleck. :wall:
Parent kennt Kinder und Kinder kennen Parent und Root. Und das ohne böse Casts, um die Referenzählung des ARC zu umgehen und so, dass der Code deiner Componente auch mit etwas älteren Delphis noch kompatibel ist, und ohne dass der halbe Code nur noch aus IFDEFS besteht. [WeakRef] kann/konnte man nicht verwenden, da es das unter Windows nicht gibt/gab. Und [NoRef] vermisse ich sowieso. |
AW: Meine neuste ARC-Hölle
Hier nochmal ein Minimalbeispiel (ich mach beim ARC Zeugs testen seit 10.2 immer gern Programme die ich kurz auf meine Ubuntu VM feuern kann, daher ohne FMX)
Delphi-Quellcode:
Da anonyme Methoden immer mehr Einzug halten (auch im offiziellen Delphi Code) halte ich diesen Defekt schon für äußerst
program Project1;
{$APPTYPE CONSOLE} uses System.SysUtils; type TTest = class(TInterfacedObject) private fProc: TProc; procedure Something; public constructor Create; destructor Destroy; override; end; var destroyCalled: Boolean; { TTest } constructor TTest.Create; begin fProc := Something; (* actually compiled into fProc := procedure begin begin Self.Something; // <- causes Self to be captured // all captured fields are strong references and thus TTest keeps itself alife // with the circular reference: fProc <-> Self end; *) end; destructor TTest.Destroy; begin destroyCalled := True; inherited; end; procedure TTest.Something; begin end; procedure Main; var intf: IInterface; begin intf := TTest.Create; intf := nil; Assert(destroyCalled); end; begin Main; end. kritisch (ja, man kann drumherum arbeiten) gerade da er schon einiges an Wissen über die Interna vorraussetzt. Also bitte fleißig voten sofern möglich :) Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:16 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