Referenzzyklen in Swift treten auf, wenn Eigenschaften von Referenztypen sich gegenseitig stark beeinflussen (oder mit Schließungen).
Besteht jedoch die Möglichkeit, Referenzzyklen mit Werttypen nur zu haben?
Ich habe dies in Spielplatz ohne Erfolg versucht ( Error: Recursive Werttyp 'A' ist nicht erlaubt ).
%Vor%Ein Referenzzyklus (oder Retain-Zyklus ) wird so benannt, weil er ein cycle im Objektdiagramm :
Jeder Pfeil zeigt ein Objekt an, das ein anderes enthält (eine starke Referenz ). Wenn der Zyklus nicht unterbrochen wird, wird der Speicher für diese Objekte niemals freigegeben.
Beim Erfassen und Speichern von Werttypen (Strukturen und Aufzählungen) gibt keine Referenz . Werte werden kopiert anstatt referenziert, obwohl Werte Referenzen auf Objekte enthalten können.
Mit anderen Worten: Werte können im Objektdiagramm ausgehende Pfeile enthalten, aber keine eingehenden Pfeile. Das bedeutet, dass sie nicht an einem Zyklus teilnehmen können.
Wie der Compiler es dir gesagt hat, ist das, was du versuchst, illegal. Genau, weil ein Werttyp ist, gibt es keine kohärente, effiziente Möglichkeit, das zu implementieren, was Sie beschreiben. Wenn ein Typ auf sich selbst verweisen muss (z. B. wenn er eine Eigenschaft besitzt, die dem eigenen Typ entspricht), verwenden Sie eine Klasse und keine Struktur.
Alternativ können Sie eine Enumeration verwenden, aber nur in einer speziellen, beschränkten Weise: Der zugehörige Wert eines Enum-Falls kann eine Instanz dieser Enumeration sein, vorausgesetzt, der Fall (oder die gesamte Enumeration) ist vorhanden markiert indirect
:
Disclaimer: Ich mache hier eine (hoffentlich erzogene) Vermutung über die inneren Abläufe des Swift-Compilers, also wendet Salzkörner an.
Fragen Sie sich neben der Wertesemantik: Warum haben wir Strukturen? Was ist der Vorteil?
Ein Vorteil ist, dass wir sie auf dem Stapel (bzw. in einem Objektrahmen) speichern können (lesen: wollen), also genau wie primitive Werte in anderen Sprachen. Insbesondere möchten wir keinen dedizierten Speicherplatz auf dem Heap zuweisen, auf den verwiesen werden soll. Das macht den Zugriff auf Struct-Werte effizienter: Wir (also der Compiler) wissen immer, wo genau im Speicher der Wert gefunden wird, relativ zum aktuellen Frame oder Objektzeiger.
Damit dies für den Compiler funktioniert, muss wissen, wieviel Platz für einen gegebenen Strukturwert reserviert werden muss, wenn die Struktur des Stacks oder Objektrahmens bestimmt wird. Solange Strukturwerte Bäume fester Größe sind (abgesehen von ausgehenden Verweisen auf Objekte; sie zeigen auf den Haufen, sind sie für uns nicht von Interesse), ist das in Ordnung: Der Compiler kann einfach alle gefundenen Größen addieren.
Wenn Sie eine rekursive Struktur hatten, schlägt dies fehl: Sie können auf diese Weise Listen oder Binärbäume implementieren. Der Compiler kann statisch nicht herausfinden, wie solche Werte im Speicher gespeichert werden, also müssen wir sie verbieten.
Nota bene: Die gleiche Argumentation erklärt, warum Strukturen pass-by-value sind: wir brauchen sie, physisch in ihrem neuen Kontext zu sein .
Normalerweise können Sie keinen Referenzzyklus mit Werttypen haben, weil Swift normalerweise keine Referenzen auf Werttypen erlaubt. Alles ist kopiert.
Wenn Sie jedoch neugierig sind, können Sie tatsächlich einen Referenzzyklus vom Werttyp induzieren, indem Sie self in einem Abschluss erfassen.
Das Folgende ist ein Beispiel. Beachten Sie, dass die Klasse MyObject
nur zur Veranschaulichung des Lecks vorhanden ist.
Ausgabe:
%Vor%Besteht jedoch die Möglichkeit, Referenzzyklen nur mit Werttypen zu haben?
Hängt davon ab, was Sie mit "nur Werttypen" meinen. Wenn Sie komplett keine Referenz einschließlich versteckter im Inneren meinen, dann ist die Antwort NEIN. Um einen Referenzzyklus zu erstellen, benötigen Sie mindestens eine Referenz.
Aber in Swift, Array, String oder anderen Typen sind Werttypen, die Referenzen innerhalb ihrer Instanzen enthalten können. Wenn Ihre "Werttypen" solche Typen enthalten, lautet die Antwort JA.
Schneller und einfacher Hack-Workaround: Einfach in ein Array einbetten.
%Vor%Tags und Links swift automatic-ref-counting value-type reference-type reference-counting