Dyb kopi vs. overfladisk kopi - og hvordan du kan bruge dem i Swift

Kopiering af et objekt har altid været en væsentlig del af kodningsparadigmet. Det være sig i Swift, Objective-C, JAVA eller ethvert andet sprog, vi bliver altid nødt til at kopiere et objekt til brug i forskellige sammenhænge.
I denne artikel vil vi diskutere detaljeret, hvordan man kopierer forskellige datatyper i Swift, og hvordan de opfører sig under forskellige omstændigheder.
Værdi og referencetyper
Alle datatyper i Swift falder stort set i to kategorier, nemlig værdityper og referencetyper .
- Værditype - hver forekomst opbevarer en unik kopi af sine data. Datatyper, der falder ind under denne kategori inkluderer -
all the basic data types, struct, enum, array, tuples
. - Referencetype - forekomster deler en enkelt kopi af dataene, og typen defineres normalt som en
class
.
Det mest karakteristiske ved begge typer ligger i deres kopieringsadfærd.
Hvad er dyb og lav kopi?
En forekomst, hvad enten det er en værditype eller en referencetype, kan kopieres på en af følgende måder:
Dyb kopi - Dupliserer alt
- Med en dyb kopi kopieres ethvert objekt, som kilden peger på, og destinationen peger på kopien. Så der oprettes to helt separate objekter.
- Samlinger - En dyb kopi af en samling er to samlinger med alle elementerne i den originale samling duplikeret.
- Mindre tilbøjelige til race betingelser og klarer sig godt i et multitrådet miljø - ændringer i et objekt har ingen effekt på et andet objekt.
- Værdityper kopieres dybt.
I ovenstående kode,
- Linje 1 :
arr1
- array (en værditype) af strenge - Linje 2 :
arr1
er tildelt tilarr2
. Dette opretter en dyb kopi afarr1
og tildeler derefter den kopi tilarr2
- Linje 7 til 11 : ændringer foretaget i
arr2
afspejler ikke iarr1
.
Dette er hvad dyb kopi er - helt separate tilfælde. Det samme koncept fungerer med alle værdityper.
I nogle scenarier, det er når en værditype indeholder indlejrede referencetyper, afslører dyb kopi en anden form for adfærd. Vi ser det i kommende sektioner.
Lav kopi - Kopier så lidt som muligt
- Med en lav kopi peges ethvert objekt, som kilden peger på, også af destinationen. Så der oprettes kun et objekt i hukommelsen.
- Samlinger - En lav kopi af en samling er en kopi af samlingsstrukturen og ikke elementerne. Med en lav kopi deler to samlinger nu de enkelte elementer.
- Hurtigere - kun referencen kopieres.
- Kopiering af referencetyper opretter en lav kopi.
I ovenstående kode,
- Linje 1 til 8 :
Address
klassetype - Linje 10 :
a1
- en forekomst afAddress
typen - Linie 11 :
a1
er tildelt tila2
. Dette opretter en lav kopi afa1
og tildeler derefter kopien tila2
, det er kun referencen, der kopieres tila2
. - Linje 16 til 19 : ændringer foretaget i
a2
vil helt sikkert afspejle sig ia1
.

I ovenstående illustration kan vi se det begge a1
og a2
pege på den samme hukommelsesadresse.
Kopiering af referencetyper dybt
Fra nu af ved vi, at når vi prøver at kopiere en referencetype, kopieres kun referencen til objektet. Intet nyt objekt oprettes. Hvad hvis vi vil oprette et helt separat objekt?
Vi kan oprette en dyb kopi af referencetypen ved hjælp af copy()
metoden. I henhold til dokumentationen
copy () - Returnerer det objekt, der returneres af copy(with:)
.
Dette er en bekvemmelighed metode til klasser, der vedtager NSCopying
protokollen. En undtagelse hæves, hvis der ikke er nogen implementering for copy(with:)
.
Lad os omstrukturere det, Address class
vi oprettede i Code Snippet 2, så det er i overensstemmelse med NSCopying
protokollen.
I ovenstående kode,
- Linje 1 til 14 :
Address
klassetypen er i overensstemmelse medNSCopying
og implementerercopy(with:)
metoden - Linie 16 :
a1
- en forekomst afAddress
typen - Linje 17 :
a1
tildeles veda2
hjælp afcopy()
metode. Dette vil skabe en dyb kopi afa1
og derefter tildele den kopi tila2
, det vil sige et helt nyt objekt oprettes. - Linie 22 til 25 : ændringer foretaget i
a2
afspejler ikke ia1
.

Som det fremgår af ovenstående illustration, både a1
og a2
pege på forskellige lagerpladser.
Lad os se på et andet eksempel. Denne gang får vi se, hvordan det fungerer med indlejrede referencetyper - en referencetype, der indeholder en anden referencetype .
I ovenstående kode,
- Linie 22: en dyb kopi af
p1
tildeles vedp2
hjælp afcopy()
metoden. Dette indebærer, at enhver ændring i en af dem ikke må have nogen indvirkning på den anden. - Linie 27 til 28:
p2’s
name
ogcity
værdierne ændres. Disse må ikke afspejle sig ip1
. - Linie 30:
p1’s
name
er som forventet, men denscity
? Det burde være,“Mumbai”
skulle det ikke være? Men vi kan ikke se, at det sker.“Bangalore”
var kun tilp2
højre? Yup ... nøjagtigt.?
Dyb kopi ...! ? T hat var ikke forventet fra dig. Du sagde, du vil kopiere alt. Og nu opfører du dig sådan. Hvorfor åh hvorfor ..?! Hvad gør jeg nu? ☠ ️
Gå ikke i panik. Lad os se på, hvad hukommelsesadresser har at sige i dette.

Fra ovenstående illustration kan vi se det
p1
ogp2
peg på forskellige hukommelsesplaceringer som forventet.- Men deres
address
variabler peger stadig på den samme placering. Det betyder, at selv efter at have kopieret dem dybt, kopieres kun referencerne - det vil sige en lav kopi selvfølgelig.
Bemærk: hver gang vi kopierer en referencetype, oprettes en lav kopi som standard, indtil vi udtrykkeligt angiver, at den skal kopieres dybt.
func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}
I ovenstående metode, vi implementerede tidligere for Person
klassen, har vi oprettet en ny forekomst ved at kopiere adressen med self.address
. Dette kopierer kun henvisningen til adresseobjektet. Dette er grunden til, at begge p1
og p2’s
address
peger på den samme placering.
Så kopiering af objektet ved hjælp af copy()
metoden opretter ikke en ægte dyb kopi af objektet .
For at duplikere et referenceobjekt fuldstændigt: referencetypen sammen med alle de indlejrede referencetyper skal kopieres med copy()
metoden.
let person = Person(self.name, self.address.copy() as? Address)
Brug af ovenstående kode i func copy(with zone: NSZone? = nil) ->
Enhver metode får alt til at fungere. Du kan se det fra nedenstående illustration.

True Deep Copy - Reference- og værdityper
Vi har allerede set, hvordan vi kan oprette en dyb kopi af referencetyperne. Selvfølgelig kan vi gøre det med alle de indlejrede referencetyper.
Men hvad med den indlejrede referencetype i en værditype, det vil sige en række objekter eller en referencetypevariabel i en struktur eller måske en tuple? Kan vi også løse det ved hjælp af copy()
? Nej, det kan vi faktisk ikke. Den copy()
metode kræver at gennemføre NSCopying
protokol, der kun virker for NSObject
underklasser. Værdityper understøtter ikke arv, så vi kan ikke bruge copy()
dem.
I linje 2 er kun strukturen arr1
kopieret dybt, men Address
genstandene inde i den er stadig lavt kopieret. Du kan se det fra nedenstående hukommelseskort.

Elementerne i begge arr1
og arr2
begge peger på de samme hukommelsesplaceringer. Dette skyldes samme grund - referencetyper kopieres som standard.
Serialisering og derefter de-serialisering af et objekt skaber altid et helt nyt objekt. Det er gyldigt for både værdityper og referencetyper.
Her er nogle API'er, som vi kan bruge til at serialisere og de-serialisere data:
- NSCoding - En protokol, der muliggør kodning og afkodning af et objekt til arkivering og distribution. Det fungerer kun med
class
typeobjekter, som det kræver arv fraNSObject
. - Codable - Gør dine datatyper kodelige og dekodbare for kompatibilitet med eksterne repræsentationer som JSON. Det fungerer for begge værdityper -
struct, array, tuple, basic data types
såvel som referencetyper -class
.
Lad os omstrukturere Address
klassen lidt længere for at overholde Codable
protokollen og fjerne al den NSCopying
kode, som vi tilføjede tidligere i Code Snippet 3.
I ovenstående kode opretter linje 11–13 en ægte dyb kopi af arr1
. Nedenfor er illustrationen, der giver et klart billede af hukommelsesplaceringerne.

Kopiér på skriv
Copy on write er en optimeringsteknik, der hjælper med at øge ydeevnen ved kopiering af værdityper.
Lad os sige, at vi kopierer en enkelt streng eller Int eller måske en hvilken som helst anden værditype - vi står ikke over for afgørende præstationsproblemer i så fald. Men hvad med når vi kopierer en række tusindvis af elementer? Vil det stadig ikke skabe problemer med ydeevnen? Hvad hvis vi bare kopierer den og ikke foretager nogen ændringer i den kopi? Er det ikke den ekstra hukommelse, vi brugte bare spild i så fald?
Her kommer begrebet Copy in Write - når du kopierer, peger hver reference på den samme hukommelsesadresse. Det er først, når en af referencerne ændrer de underliggende data, at Swift faktisk kopierer den oprindelige forekomst og foretager ændringen.
Uanset om det er dyb kopi eller lav kopi, oprettes der ikke en ny kopi, før vi foretager en ændring i et af objekterne.
I ovenstående kode,
- Linje 2 : en dyb kopi af
arr1
er tildeltarr2
- Linie 4 og 5 :
arr1
ogarr2
peger stadig på den samme hukommelsesadresse - Linie 7 : ændringer foretaget i
arr2
- Linie 9 og 10 :
arr1
ogarr2
peger nu på forskellige hukommelsesplaceringer
Nu ved du mere om dybe og lave kopier, og hvordan de opfører sig i forskellige scenarier med forskellige datatyper. Du kan prøve dem med dit eget sæt eksempler og se, hvilke resultater du får.
Yderligere læsning
Glem ikke at læse mine andre artikler:
- Alt om Codable i Swift 4
- Alt, hvad du altid har ønsket at vide om meddelelser i iOS
- Farvelæg det med GRADIENTS - iOS
- Kodning til iOS 11: Sådan trækkes og droppes i samlinger og tabeller
- Alt hvad du behøver at vide om Today Extensions (Widget) i iOS 10
- UICollectionViewCell-valg gjort let .. !!
Du er velkommen til at efterlade kommentarer, hvis du har spørgsmål.