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 : arr1er tildelt til arr2. Dette opretter en dyb kopi af arr1og tildeler derefter den kopi tilarr2
  • Linje 7 til 11 : ændringer foretaget i arr2afspejler ikke i arr1.

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 : Addressklassetype
  • Linje 10 : a1- en forekomst af Addresstypen
  • Linie 11 : a1er tildelt til a2. Dette opretter en lav kopi af a1og tildeler derefter kopien til a2, det er kun referencen, der kopieres til a2.
  • Linje 16 til 19 : ændringer foretaget i a2vil helt sikkert afspejle sig i a1.

I ovenstående illustration kan vi se det begge a1og a2pege 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 NSCopyingprotokollen. En undtagelse hæves, hvis der ikke er nogen implementering for copy(with:).

Lad os omstrukturere det, Address classvi oprettede i Code Snippet 2, så det er i overensstemmelse med NSCopyingprotokollen.

I ovenstående kode,

  • Linje 1 til 14 : Addressklassetypen er i overensstemmelse med NSCopyingog implementerer copy(with:)metoden
  • Linie 16 : a1- en forekomst af Addresstypen
  • Linje 17 : a1tildeles ved a2hjælp af copy()metode. Dette vil skabe en dyb kopi af a1og derefter tildele den kopi til a2, det vil sige et helt nyt objekt oprettes.
  • Linie 22 til 25 : ændringer foretaget i a2afspejler ikke i a1.

Som det fremgår af ovenstående illustration, både a1og a2pege 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 p1tildeles ved p2hjælp af copy()metoden. Dette indebærer, at enhver ændring i en af ​​dem ikke må have nogen indvirkning på den anden.
  • Linie 27 til 28:p2’sname og cityværdierne ændres. Disse må ikke afspejle sig i p1.
  • Linie 30:p1’sname er som forventet, men dens city? Det burde være, “Mumbai”skulle det ikke være? Men vi kan ikke se, at det sker. “Bangalore”var kun til p2hø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

  • p1og p2peg på forskellige hukommelsesplaceringer som forventet.
  • Men deres addressvariabler 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 Personklassen, 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 p1og p2’saddresspeger 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 NSCopyingprotokol, der kun virker for NSObjectunderklasser. Værdityper understøtter ikke arv, så vi kan ikke bruge copy()dem.

I linje 2 er kun strukturen arr1kopieret dybt, men Addressgenstandene inde i den er stadig lavt kopieret. Du kan se det fra nedenstående hukommelseskort.

Elementerne i begge arr1og arr2begge 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:

  1. NSCoding - En protokol, der muliggør kodning og afkodning af et objekt til arkivering og distribution. Det fungerer kun med classtypeobjekter, som det kræver arv fra NSObject.
  2. 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 typessåvel som referencetyper - class.

Lad os omstrukturere Addressklassen lidt længere for at overholde Codableprotokollen og fjerne al den NSCopyingkode, 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 arr1er tildeltarr2
  • Linie 4 og 5 : arr1og arr2peger stadig på den samme hukommelsesadresse
  • Linie 7 : ændringer foretaget iarr2
  • Linie 9 og 10 : arr1og arr2peger 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:

  1. Alt om Codable i Swift 4
  2. Alt, hvad du altid har ønsket at vide om meddelelser i iOS
  3. Farvelæg det med GRADIENTS - iOS
  4. Kodning til iOS 11: Sådan trækkes og droppes i samlinger og tabeller
  5. Alt hvad du behøver at vide om Today Extensions (Widget) i iOS 10
  6. UICollectionViewCell-valg gjort let .. !!

Du er velkommen til at efterlade kommentarer, hvis du har spørgsmål.