Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Cross-Platform-Entwicklung (https://www.delphipraxis.net/91-cross-platform-entwicklung/)
-   -   iOS Problem mit ARC unter iOS? (https://www.delphipraxis.net/187990-problem-mit-arc-unter-ios.html)

romber 21. Jan 2016 16:25

Problem mit ARC unter iOS?
 
Hallo!

Ich programmiere eine iOS-App unter Verwendung der D.P.F Delphi iOS Native Components. Ich habe eine TableView, die dynamisch mit Daten befüllt wird. Beim Markieren eines Items wechselt die App zu einer anderen dynamisch erzeugten View (Details View), auf die ich nach Bedarf mehrere danymisch erzeugte Controls setze. Beim wechseln zurück zur TableView sollen alle dynamisch erzeugte Controls wieder freigegeben werden. So wie sich meine App nach mehreren aufrufen der Details View verhält habe ich das Gefühl, dass die erzeugte Controls gar nicht freigegeben werden. Nach ca. 50-70 Aufrufen stürzt die App endgültig ab.

Gibt es eine verlässliche Methode, mit der man überprüfen kann, ob dynamisch erzeugte Controls auf einer dynamisch erzeugten View von ARC freigegeben werden?

Darlo 21. Jan 2016 16:42

AW: Problem mit ARC unter iOS?
 
Wie gibts Du die Controls denn frei? Du kannst mit den Instruments von xCode verfolgen was genau wann passiert (Memory).

romber 21. Jan 2016 17:00

AW: Problem mit ARC unter iOS?
 
Ich setze am Ende der Methode (in der die Controls alle deklariert und erstellt werden) alle Controls auf nil und gehe davon aus, dass ARC das Control bei der nächsten Gelegenheit wieder freigibt.

bra 21. Jan 2016 17:05

AW: Problem mit ARC unter iOS?
 
Wie Darlo schon geschrieben hat, lässt sich das am einfachsten mit Instruments verfolgen, da kann man sich u.a. die komplette Struktur der noch vorhandenen Objekte anzeigen lassen.

Ein Objekt auf nil zu setzen reicht bei ARC nicht unbedingt. Solange noch irgendwelche Verweise auf das Objekt vorhanden sind, wird es nicht freigegeben. Ich habe mir dazu auch zu Debugging-Zwecken eine eigene Routine erstellt:

Code:
procedure TryFree(const AUnitName: String; const AObj: TObject);
begin
  if not Assigned(AObj) then
    Exit;
{$IFDEF AUTOREFCOUNT}
  if AObj.RefCount > 1 then begin
    OutputDebugStrFmt('[[%s]] %s.RefCount=%d', [AUnitName, AObj.QualifiedClassName, AObj.RefCount]);
    AObj.DisposeOf;
  end;
{$ELSE}
  AObj.Free;
{$ENDIF AUTOREFCOUNT}
end;
Die ist ziemlich nützlich, auch um Leaks in den Delphi-Komponenten zu finden.

romber 21. Jan 2016 17:25

AW: Problem mit ARC unter iOS?
 
Zitat:

Zitat von bra (Beitrag 1327752)
Ein Objekt auf nil zu setzen reicht bei ARC nicht unbedingt. Solange noch irgendwelche Verweise auf das Objekt vorhanden sind, wird es nicht freigegeben.

Das stimmt. Aber die Controls werden in meinem Fall in der Methode lokal deklariert und nur in dieser Methode benutzt. Neben Controls selber setze ich auch noch für jedes Control den Parent auf nil.

Zitat:

Zitat von bra (Beitrag 1327752)
...Ich habe mir dazu auch zu Debugging-Zwecken eine eigene Routine erstellt

Vielen Dank dafür! Werde ich gleich ausprobieren!

Sir Rufo 21. Jan 2016 17:42

AW: Problem mit ARC unter iOS?
 
Man braucht keine Unterscheidung zu machen.

Delphi-Quellcode:
TObject.DisposeOf
testet selber auf
Delphi-Quellcode:
nil
und ruft den Destructor auf (egal ob ARC oder nicht).

Delphi-Quellcode:
TObject.Free
testet selber auf
Delphi-Quellcode:
nil
und ruft den Destructor auf (nur bei NICHT-ARC)

Will man also eine Komponente ins Jenseits befördern, nimmt man generell
Delphi-Quellcode:
DisposeOf
.
Genauso mit Streams und sonstigen Instanzen, die noch Ressourcen halten, die man jetzt gesichert freigeben möchte.

Für den Rest nimmt man
Delphi-Quellcode:
Free
oder
Delphi-Quellcode:
FreeAndNil
.

romber 21. Jan 2016 18:00

AW: Problem mit ARC unter iOS?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1327756)
Will man also eine Komponente ins Jenseits befördern, nimmt man generell
Delphi-Quellcode:
DisposeOf
.

Werden dabei auch all die Komponenten freigegeben, die in jenseits zu befördernde Komponente als Owner referenzieren?
Was passiert, wenn auf dem mit DisposeOf gekillten Control andere Controls liegen, die es als Parent referenzieren?

Sir Rufo 21. Jan 2016 18:46

AW: Problem mit ARC unter iOS?
 
Wird eine
Delphi-Quellcode:
TComponent
Instanz zerstört werden auch alle Components zerstört.
Wird eine
Delphi-Quellcode:
TWinControl
Instanz zerstört werden auch alle Children zerstört.
Wird eine
Delphi-Quellcode:
TFmxObject
Instanz zerstört werden auch alle Children zerstört.

Delphi-Quellcode:
TObject.DisposeOf
zerstört ohne Wenn und Aber (ARC oder nicht).

Einfache und alte Regel ... nur das mit dem
Delphi-Quellcode:
DisposeOf
ist neu.

romber 21. Jan 2016 19:59

AW: Problem mit ARC unter iOS?
 
Zitat:

Zitat von bra (Beitrag 1327752)
Code:
procedure TryFree(const AUnitName: String; const AObj: TObject);
begin
  if not Assigned(AObj) then
    Exit;
{$IFDEF AUTOREFCOUNT}
  if AObj.RefCount > 1 then begin
    OutputDebugStrFmt('[[%s]] %s.RefCount=%d', [AUnitName, AObj.QualifiedClassName, AObj.RefCount]);
    AObj.DisposeOf;
  end;
{$ELSE}
  AObj.Free;
{$ENDIF AUTOREFCOUNT}
end;

Wo kommt denn die OutputDebugStrFmt her? Kann nichts dazu finden.

romber 21. Jan 2016 20:53

AW: Problem mit ARC unter iOS?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1327766)
Wird eine
Delphi-Quellcode:
TComponent
Instanz zerstört werden auch alle Components zerstört.
Wird eine
Delphi-Quellcode:
TWinControl
Instanz zerstört werden auch alle Children zerstört.
Wird eine
Delphi-Quellcode:
TFmxObject
Instanz zerstört werden auch alle Children zerstört.

Delphi-Quellcode:
TObject.DisposeOf
zerstört ohne Wenn und Aber (ARC oder nicht).

Einfache und alte Regel ... nur das mit dem
Delphi-Quellcode:
DisposeOf
ist neu.

Bei mir führt DisposeOf zum Absturz der App. Und zwar prüfe ich zuerst, ob DetailsView bereits existiert und kille diese ggfs. mit DisposeOf. Danach erstelle ich die DetailsView wieder. Sobald ich Controls auf das neu erzeugte DetailsView setze, stürzt die App ab.

himitsu 21. Jan 2016 23:42

AW: Problem mit ARC unter iOS?
 
Und was sagt der Debugger?
Also WO stürzt es denn ab?

Bei TComponents gibt es auch noch Verbindungen zwischen den Controls, welche Referenzen darstellen und somit die Komponenten am Leben erhalten.
Und wenn du dir wirklich nicht sicher bist, ob etwas freigegeben wird, dann bau z.B. eine MessageBox/Logging in den Destructor und fertig.

Und warum verdammt nochmal ist man auf diese bescheuerte Idee mit dem DisposeOf gekommen und hat nicht einfach auch im ARC mit Free das "wirklich" freigeben können?
Also das aus DisposeOf ins Free eingebaut.

Sir Rufo 22. Jan 2016 02:03

AW: Problem mit ARC unter iOS?
 
Delphi-Referenz durchsuchenTObject->Delphi-Referenz durchsuchenTPersistent->Delphi-Referenz durchsuchenTComponent->Delphi-Referenz durchsuchenTFmxObject->Delphi-Referenz durchsuchenTControl
Darum gelten für ein
Delphi-Quellcode:
TControl
die Regeln für
Delphi-Quellcode:
TComponent
und
Delphi-Quellcode:
TFmxObject
:stupid:

Weil unter ARC die Uhren anders ticken braucht man eine andere Strategie was den Destructor angeht.
Der Destructor wird unter ARC automatisch aufgerufen, wenn der RefCount auf 0 geht (keiner will mehr mit der Instanz spielen).

Würde jetzt mit
Delphi-Quellcode:
Free
unter ARC auch der Destructor aufgerufen, dann würde dieser mindestens zweimal aufgerufen:
Delphi-Quellcode:
var
  foo: TFoo;

foo.Free; // Destructor wird aufgerufen (ohne ARC)
foo := nil; // Destructor wird aufgerufen (mit ARC)
Ist das auch jedem bewusst? Denn das wäre ein anderes Verhalten als man es gewohnt ist.

Darum gibt es die
Delphi-Quellcode:
DisposeOf
Methode, weil ich als Programmierer damit (hoffentlich) ganz bewusst den Doppelaufruf des Destructors akzeptiere.
Delphi-Quellcode:
var
  foo: TFoo;

foo.DisposeOf; // Destructor wird aufgerufen (immer)
foo := nil; // Destructor wird aufgerufen (ARC)
Im Destructor kann man noch den Status Delphi-Referenz durchsuchenTObject.Disposed abfragen um eine Aktion auch garantiert nur einmal auszuführen.
Delphi-Quellcode:
destructor TFoo.Destroy;
begin
  CloseHandle( FHandle );
  inherited;
end;
Wer sieht das Problem?

Unter ARC könnte ich damit ein Handle schliessen, was ich aber gar nicht schliessen möchte.

Der Destructor sollte so aussehen
Delphi-Quellcode:
destructor TFoo.Destroy;
begin
  if not Disposed then
  begin
    CloseHandle( FHandle );
  end;
  inherited;
end;
denn nur so wird das Freigeben des Handle auch nur einmal ausgeführt.

PS Die Beispiele in der Dokumentation von Emba beinhalten einen Fehler, denn die Eigenschaft Delphi-Referenz durchsuchenTObject.Disposed ist
Delphi-Quellcode:
protected
und kann von aussen gar nicht abgefragt werden :roll:

bra 22. Jan 2016 08:59

AW: Problem mit ARC unter iOS?
 
[QUOTE=romber;1327772]
Zitat:

Zitat von bra (Beitrag 1327752)
Wo kommt denn die OutputDebugStrFmt her? Kann nichts dazu finden.

Das ist eine eigene Routine von uns, die einfach nur eine Meldung ins iOS-Log ausgibt.

himitsu 22. Jan 2016 09:07

AW: Problem mit ARC unter iOS?
 
Man hätte aber das "abweichende" Verhalten anders benennen können, anstatt das Standardverhalten zu ändern.
Von mir aus auch einen WirklichGenauJetztFreigeben-Parameter beim Free mit dran (Default False?)

Halbwegs abwärtskompatiblen Code kann man so schon lange vergessen und Crossplattform ist auch nicht grade einfach.

Sir Rufo 22. Jan 2016 09:22

AW: Problem mit ARC unter iOS?
 
Zitat:

Zitat von himitsu (Beitrag 1327801)
Man hätte aber das "abweichende" Verhalten anders benennen können, anstatt das Standardverhalten zu ändern.
Von mir aus auch einen WirklichGenauJetztFreigeben-Parameter beim Free mit dran (Default False?)

Halbwegs abwärtskompatiblen Code kann man so schon lange vergessen und Crossplattform ist auch nicht grade einfach.

Was ist denn jetzt wirklich abweichend?

Mit
Delphi-Quellcode:
Free
wird der Destructor nur einmal durchlaufen. Da hat sich nichts geändert.

Was ist denn gefährlicher?
  • Ein Programm mit Speicherlecks
  • Ein Programm was auf einmal falsch arbeitet?
Ich bin da eher für die Speicherlecks.

Man muss sich vor Augen halten, was eine Methode denn wirklich macht/bedeutet, dann ist so ein Paradigmenwechsel auch nicht mehr so schwer.

Grundsätzlich wäre ich aber für die generelle Einführung von ARC auf jeder Plattform. Dieser Mischmasch bei einer Multiplattform Anwendung bringt einen eher um den Verstand.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:27 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