von Payal Gupta

Das Kopieren eines Objekts war schon immer ein wesentlicher Bestandteil des Codierungsparadigmas. Sei es in Swift, Objective-C, JAVA oder einer anderen Sprache, wir müssen immer ein Objekt für die Verwendung in verschiedenen Kontexten kopieren.
In diesem Artikel wird ausführlich erläutert, wie verschiedene Datentypen in Swift kopiert werden und wie sie sich unter verschiedenen Umständen verhalten.
Wert- und Referenztypen
Alle Datentypen in Swift lassen sich grob in zwei Kategorien einteilen, nämlich Werttypen und Referenztypen.
- Werttyp – Jede Instanz speichert eine eindeutige Kopie ihrer Daten. Zu den Datentypen, die in diese Kategorie fallen, gehören —
all the basic data types, struct, enum, array, tuples
. - Referenztyp – Instanzen teilen sich eine einzelne Kopie der Daten, und der Typ wird normalerweise als
class
definiert.
Das Unterscheidungsmerkmal beider Typen liegt in ihrem Kopierverhalten.
Was ist tiefe und flache Kopie?
Eine Instanz, unabhängig davon, ob es sich um einen Werttyp oder einen Referenztyp handelt, kann auf eine der folgenden Arten kopiert werden:
Deep Copy — Dupliziert alles
- Mit einer Deep Copy wird jedes Objekt, auf das die Quelle zeigt, kopiert und die Kopie vom Ziel. Es werden also zwei völlig separate Objekte erstellt.
- Sammlungen – Eine tiefe Kopie einer Sammlung besteht aus zwei Sammlungen, wobei alle Elemente der ursprünglichen Sammlung dupliziert werden.
- Weniger anfällig für Race-Bedingungen und gute Leistung in einer Multithread-Umgebung – Änderungen an einem Objekt haben keine Auswirkungen auf ein anderes Objekt.
- Werttypen werden tief kopiert.
Im obigen Code,
- Zeile 1:
arr1
– Array (ein Werttyp) von Zeichenfolgen - Zeile 2:
arr1
istarr2
zugewiesen. Dadurch wird eine tiefe Kopie vonarr1
erstellt und diese Kopie dann den Zeilen 7 bis 11 vonarr2
- zugewiesen: Alle in
arr2
vorgenommenen Änderungen spiegeln sich nicht inarr1
wider.
Das ist Deep Copy – völlig getrennte Instanzen. Das gleiche Konzept funktioniert mit allen Werttypen.
In einigen Szenarien, d. h. wenn ein Werttyp verschachtelte Referenztypen enthält, zeigt Deep Copy ein anderes Verhalten. Wir werden das in den kommenden Abschnitten sehen.
Flache Kopie – Dupliziert so wenig wie möglich
- Bei einer flachen Kopie wird jedes Objekt, auf das die Quelle zeigt, auch vom Ziel angezeigt. Es wird also nur ein Objekt im Speicher erstellt.
- Sammlungen – Eine flache Kopie einer Sammlung ist eine Kopie der Sammlungsstruktur, nicht der Elemente. Mit einer flachen Kopie teilen sich nun zwei Sammlungen die einzelnen Elemente.
- Schneller – nur die Referenz wird kopiert.
- Beim Kopieren von Referenztypen wird eine flache Kopie erstellt.
Im obigen Code,
- Zeilen 1 bis 8:
Address
Klassentyp - Zeile 10:
a1
— Eine Instanz vonAddress
Typ - Zeile 11:
a1
ista2
zugewiesen. Dadurch wird eine flache Kopie vona1
erstellt und diese Kopie danna2
zugewiesen, dh nur die Referenz wird ina2
kopiert. - Zeilen 16 bis 19: alle Änderungen, die in
a2
vorgenommen wurden, werden sich sicherlich ina1
widerspiegeln.

In der obigen Abbildung können wir sehen, dass sowohl a1
als auch a2
auf dieselbe Speicheradresse zeigen.
Referenztypen kopieren >
Ab sofort wissen wir, dass jedes Mal, wenn wir versuchen, einen Referenztyp zu kopieren, nur der Verweis auf das Objekt kopiert wird. Es wird kein neues Objekt erstellt. Was ist, wenn wir ein völlig separates Objekt erstellen möchten?
Mit der Methode copy()
können wir eine tiefe Kopie des Referenztyps erstellen. Gemäß der Dokumentation
copy() – Gibt das von copy(with:)
zurückgegebene Objekt zurück.
Dies ist eine bequeme Methode für Klassen, die das NSCopying
-Protokoll verwenden. Eine Ausnahme wird ausgelöst, wenn für copy(with:)
keine Implementierung vorhanden ist.
Lassen Sie uns das Address class
, das wir in Code Snippet 2 erstellt haben, so umstrukturieren, dass es dem NSCopying
-Protokoll entspricht.
Im obigen Code,
- Zeilen 1 bis 14:
Address
Klassentyp entsprichtNSCopying
und implementiertcopy(with:)
Methode - Zeile 16:
a1
– eine Instanz vonAddress
Typ - Zeile 17:
a1
wirda2
mit der Methodecopy()
zugewiesen. Dadurch wird eine tiefe Kopie vona1
erstellt und diese Kopie danna2
zugewiesen. - Zeilen 22 bis 25: Änderungen in
a2
werden nicht ina1
wiedergegeben.

Wie aus der obigen Abbildung hervorgeht, zeigen sowohl a1
als auch a2
auf unterschiedliche Speicherorte.
Schauen wir uns ein anderes Beispiel an. Dieses Mal werden wir sehen, wie es mit verschachtelten Referenztypen funktioniert — einem Referenztyp, der einen anderen Referenztyp enthält.
Im obigen Code,
- Zeile 22: Eine tiefe Kopie von
p1
wirdp2
mit der Methodecopy()
zugewiesen. Dies bedeutet, dass jede Änderung in einem von ihnen keine Auswirkungen auf den anderen haben darf. - Zeilen 27 bis 28:
p2's
name
undcity
Werte werden geändert. Diese dürfen sich nicht inp1
widerspiegeln. - Zeile 30:
p1's
name
ist wie erwartet, aber es istcity
? Es sollte"Mumbai"
sein, nicht wahr? Aber das können wir nicht sehen."Bangalore"
war nur fürp2
richtig? Ja … genau.?
Tiefe Kopie…!? Das war nicht von dir zu erwarten. Du sagtest, du kopierst alles. Und jetzt benimmst du dich so. Warum oh warum..?! Was mache ich jetzt? ☠️
Keine Panik. Schauen wir uns an, was Speicheradressen dazu zu sagen haben.

Aus der obigen Abbildung können wir das sehen
-
p1
undp2
zeigen erwartungsgemäß auf verschiedene Speicherorte. - Aber ihre
address
Variablen zeigen immer noch auf denselben Speicherort. Dies bedeutet, dass selbst nach dem tiefen Kopieren nur die Referenzen kopiert werden – das heißt natürlich eine flache Kopie.
Bitte beachten Sie: jedes Mal, wenn wir einen Referenztyp kopieren, wird standardmäßig eine flache Kopie erstellt, bis wir explizit angeben, dass sie tief kopiert werden soll.
func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}
In der obigen Methode, die wir zuvor für die Klasse Person
implementiert haben, haben wir eine neue Instanz erstellt, indem wir die Adresse mit self.address
kopiert haben . Dadurch wird nur der Verweis auf das Adressobjekt kopiert. Dies ist der Grund, warum sowohl p1
als auch p2's
address
auf dieselbe Position zeigen.
Wenn Sie das Objekt also mit der Methode copy()
kopieren, wird keine echte tiefe Kopie des Objekts erstellt.
So duplizieren Sie ein Referenzobjekt vollständig: der Referenztyp muss zusammen mit allen verschachtelten Referenztypen mit der Methode copy()
kopiert werden.
let person = Person(self.name, self.address.copy() as? Address)
Wenn Sie den obigen Code in der func copy(with zone: NSZone? = nil) ->
-Methode verwenden, funktioniert alles. Sie können das aus der folgenden Abbildung sehen.

True Deep Copy – Referenz- und Werttypen
Wir haben bereits gesehen, wie wir eine tiefe Kopie der Referenztypen erstellen können. Natürlich können wir das mit allen verschachtelten Referenztypen tun.
Aber was ist mit dem verschachtelten Referenztyp in einem Werttyp, dh einem Array von Objekten, oder einer Referenztypvariablen in einer Struktur oder möglicherweise einem Tupel? Können wir das auch mit copy()
lösen? Nein, das können wir nicht. Die copy()
-Methode erfordert die Implementierung des NSCopying
-Protokolls, das nur für NSObject
-Unterklassen funktioniert. Werttypen unterstützen keine Vererbung, daher können wir copy()
nicht mit ihnen verwenden.
In Zeile 2 wird nur die Struktur von arr1
tief kopiert, aber die darin enthaltenen Address
-Objekte werden immer noch flach kopiert. Sie können das aus der folgenden Speicherkarte sehen.

Die Elemente in arr1
und arr2
zeigen beide auf dieselben Speicherorte. Dies hat den gleichen Grund – Referenztypen werden standardmäßig nicht kopiert.
Wenn Sie ein Objekt serialisieren und dann de-serialisieren, wird immer ein brandneues Objekt erstellt. Es ist sowohl für Werttypen als auch für Referenztypen gültig.
Hier sind einige APIs, mit denen wir Daten serialisieren und de-serialisieren können:
- NSCoding – Ein Protokoll, mit dem ein Objekt zur Archivierung und Verteilung codiert und decodiert werden kann. Es funktioniert nur mit Objekten vom Typ
class
, da vonNSObject
geerbt werden muss. - Codable – Machen Sie Ihre Datentypen für die Kompatibilität mit externen Darstellungen wie JSON codier- und decodierbar. Es funktioniert sowohl für Werttypen –
struct, array, tuple, basic data types
als auch für Referenztypen –class
.
Lassen Sie uns die Address
-Klasse etwas weiter umstrukturieren, um dem Codable
-Protokoll zu entsprechen, und den gesamten NSCopying
-Code entfernen, den wir zuvor in Code Snippet 3 hinzugefügt haben.
Im obigen Code erstellen die Zeilen 11-13 eine echte tiefe Kopie von arr1
. Unten ist die Abbildung, die ein klares Bild der Speicherorte gibt.

Copy on Write
Copy on write ist eine Optimierungstechnik, die die Leistung beim Kopieren von Werttypen verbessert.
Angenommen, wir kopieren einen einzelnen String oder Int oder einen anderen Werttyp — in diesem Fall treten keine entscheidenden Leistungsprobleme auf. Aber was ist, wenn wir ein Array von Tausenden von Elementen kopieren? Wird es immer noch keine Leistungsprobleme verursachen? Was ist, wenn wir es einfach kopieren und keine Änderungen an dieser Kopie vornehmen? Ist der zusätzliche Speicher, den wir verwendet haben, in diesem Fall nicht nur Verschwendung?
Hier kommt das Konzept des Kopierens beim Schreiben — beim Kopieren zeigt jede Referenz auf dieselbe Speicheradresse. Nur wenn eine der Referenzen die zugrunde liegenden Daten ändert, kopiert Swift die ursprüngliche Instanz und nimmt die Änderung vor.
Das heißt, ob tiefe Kopie oder flache Kopie, eine neue Kopie wird erst erstellt, wenn wir eine Änderung an einem der Objekte vornehmen.
Im obigen Code,
- Zeile 2: Eine tiefe Kopie von
arr1
wirdarr2
- Zeilen 4 und 5 zugewiesen:
arr1
undarr2
zeigen immer noch auf dieselbe Speicheradresse - Zeile 7: Änderungen in
arr2
- Zeilen 9 und 10:
arr1
undarr2
zeigen jetzt auf verschiedene Speicherorte
Jetzt wissen Sie mehr über tiefe und flache Kopien und wie sie sich in verschiedenen Szenarien mit unterschiedlichen Datentypen verhalten. Sie können sie mit Ihren eigenen Beispielen ausprobieren und sehen, welche Ergebnisse Sie erhalten.
Weiterführende Literatur
Vergessen Sie nicht, meine anderen Artikel zu lesen:
- Alles über Codable in Swift 4
- Alles, was Sie schon immer über Benachrichtigungen in iOS wissen wollten
- Färben Sie es mit FARBVERLÄUFEN — iOS
- Codierung für iOS 11: So ziehen Sie & Drop in Sammlungen & Tabellen
- Alles, was Sie über ) in iOS 10
- UICollectionViewCell Auswahl leicht gemacht..!!