av Payal Gupta

att kopiera ett objekt har alltid varit en viktig del i kodningsparadigmet. Var det i Swift, Objective-C, JAVA eller något annat språk, vi behöver alltid kopiera ett objekt för användning i olika sammanhang.

i den här artikeln kommer vi att diskutera i detalj hur man kopierar olika datatyper i Swift och hur de beter sig under olika omständigheter.

värde-och referenstyper

alla datatyper i Swift faller i stort sett i två kategorier, nämligen värdetyper och referenstyper.

  • värdetyp — varje instans behåller en unik kopia av dess data. Datatyper som faller inom denna kategori inkluderar – all the basic data types, struct, enum, array, tuples.
  • referenstyp — instanser delar en enda kopia av data, och typen definieras vanligtvis som en class.

det mest utmärkande för båda typerna ligger i deras kopieringsbeteende.

Vad är djup och grund kopia?

en instans, oavsett om det är en värdetyp eller en referenstyp, kan kopieras på något av följande sätt:

djup kopia — duplicerar allt

  • med en djup kopia kopieras alla objekt som källan pekar på och kopian pekas på av destinationen. Så två helt separata objekt kommer att skapas.
  • Samlingar-en djup kopia av en samling är två samlingar med alla element i den ursprungliga samlingen dupliceras.
  • mindre benägna att rasförhållanden och presterar bra i en flertrådad miljö — förändringar i ett objekt har ingen effekt på ett annat objekt.
  • värdetyper kopieras djupt.

i ovanstående kod,

  • rad 1: arr1 — array (en värdetyp) av strängar
  • rad 2: arr1 tilldelas arr2. Detta skapar en djup kopia av arr1 och tilldelar sedan den kopian till arr2
  • raderna 7 till 11: eventuella ändringar som gjorts i arr2 återspeglar inte i arr1 .

detta är vad djup kopia är-helt separata instanser. Samma koncept fungerar med alla värdetyper.

i vissa scenarier, det vill säga när en värdetyp innehåller kapslade referenstyper, avslöjar deep copy en annan typ av beteende. Vi får se det i kommande avsnitt.

Shallow copy — duplicerar så lite som möjligt

  • med en shallow copy pekas alla objekt som källan pekar på också av destinationen. Så bara ett objekt kommer att skapas i minnet.
  • Samlingar-en grund kopia av en samling är en kopia av samlingsstrukturen, inte elementen. Med en grund kopia delar två samlingar nu de enskilda elementen.
  • snabbare-endast referensen kopieras.
  • kopiering av referenstyper skapar en grund kopia.

i ovanstående kod,

  • raderna 1 till 8: Address klass Typ
  • rad 10: a1 — en instans av Address typ
  • rad 11: a1 tilldelas a2. Detta skapar en grund kopia av a1 och tilldelar sedan den kopian till a2, det vill säga endast referensen kopieras till a2.
  • raderna 16 till 19: alla ändringar som görs i a2 kommer säkert att återspegla i a1.

i ovanstående illustration kan vi se att både a1 och a2 pekar på samma minnesadress.

kopiera referenstyper djupt

från och med nu vet vi att när vi försöker kopiera en referenstyp kopieras endast referensen till objektet. Inget nytt objekt skapas. Vad händer om vi vill skapa ett helt separat objekt?

vi kan skapa en djup kopia av referenstypen med metoden copy(). Enligt dokumentationen returnerar

copy() — objektet som returneras av copy(with:).

detta är en bekvämlighetsmetod för klasser som antar protokollet NSCopying. Ett undantag tas upp om det inte finns någon implementering för copy(with:).

Låt oss omstrukturera Address class vi skapade i kodavsnitt 2 för att överensstämma med protokollet NSCopying.

i ovanstående kod,

  • linjerna 1 till 14: Address klass Typ överensstämmer med NSCopying och implementerar copy(with:) metod
  • linje 16: Address typ
  • linje 17: a1 tilldelas a2 med copy() – metoden. Detta skapar en djup kopia av a1 och tilldelar sedan den kopian till a2 , det vill säga ett helt nytt objekt kommer att skapas.
  • raderna 22 till 25: eventuella ändringar som gjorts i a2 kommer inte att återspeglas i a1 .

som framgår av ovanstående illustration pekar både a1 och a2 på olika minnesplatser.

Låt oss titta på ett annat exempel. Den här gången ser vi hur det fungerar med kapslade referenstyper — en referenstyp som innehåller en annan referenstyp.

i ovanstående kod,

  • rad 22: en djup kopia av p1 tilldelas p2 med metoden copy(). Detta innebär att någon förändring i en av dem inte får ha någon effekt på den andra.
  • raderna 27 till 28: p2's name och city värden ändras. Dessa får inte återspeglas i p1.
  • Linje 30: p1's name är som förväntat, men dess city? Det borde vara "Mumbai" borde det inte? Men vi kan inte se det hända. "Bangalore" var bara för p2 rätt? Japp … exakt.?

djup kopia…!? Det var inte förväntat av dig. Du sa att du skulle kopiera allt. Och nu beter du dig så här. Varför Åh varför..?! Vad gör jag nu? 2754 >

inte panik. Låt oss titta på vad minnesadresser har att säga i detta.

från ovanstående illustration kan vi se det

  • p1 och p2 peka på olika minnesplatser som förväntat.
  • men deras address variabler pekar fortfarande på samma plats. Det betyder att även efter att ha kopierat dem djupt kopieras endast referenserna — det vill säga en grund kopia förstås.

observera: varje gång vi kopierar en referenstyp skapas en grund kopia som standard tills vi uttryckligen anger att den ska kopieras djupt.

func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}

i ovanstående metod implementerade vi tidigare för klassen Person har vi skapat en ny instans genom att kopiera adressen med self.address . Detta kopierar bara referensen till adressobjektet. Detta är anledningen till att både p1 och p2's address pekar på samma plats.

så kopiering av objektet med metoden copy() skapar inte en sann djup kopia av objektet.

för att duplicera ett referensobjekt helt: referenstypen tillsammans med alla kapslade referenstyper måste kopieras med metoden copy().

let person = Person(self.name, self.address.copy() as? Address)

med ovanstående kod i func copy(with zone: NSZone? = nil) -> kommer alla metoder att få allt att fungera. Du kan se det från nedanstående illustration.

True Deep Copy-referens-och värdetyper

vi har redan sett hur vi kan skapa en djup kopia av referenstyperna. Naturligtvis kan vi göra det med alla kapslade referenstyper.

men hur är det med den kapslade referenstypen i en värdetyp, det vill säga en array av objekt, eller en referenstypvariabel i en struktur eller kanske en tupel? Kan vi lösa det med copy() också? Nej det kan vi inte, faktiskt. Metoden copy() kräver implementering av NSCopying protokoll som bara fungerar för NSObject underklasser. Värdetyper stöder inte arv, så vi kan inte använda copy() med dem.

i rad 2 är endast strukturen för arr1 djupt kopierad, men Address – objekten inuti den är fortfarande grunda kopierade. Du kan se det från nedanstående minneskarta.

elementen i både arr1 och arr2 pekar båda på samma minnesplatser. Detta beror på samma anledning — referenstyper är grunda kopierade som standard.

serialisering och avserialisering av ett objekt skapar alltid ett helt nytt objekt. Det gäller både värdetyper och referenstyper.

här är några API: er som vi kan använda för att serialisera och avserialisera data:

  1. NSCoding-ett protokoll som gör att ett objekt kan kodas och avkodas för arkivering och distribution. Det fungerar bara med class typobjekt eftersom det kräver arv från NSObject.
  2. Codable-gör dina datatyper kodabla och avkodningsbara för kompatibilitet med externa representationer som JSON. Det kommer att fungera för båda värdetyperna – struct, array, tuple, basic data typessamt referenstyper – class.

Låt oss omstrukturera klassen Address lite längre för att överensstämma med protokollet Codable och ta bort all kod NSCopying som vi lade till tidigare i kodavsnitt 3.

i ovanstående kod kommer raderna 11-13 att skapa en sann djup kopia av arr1. Nedan är illustrationen som ger en tydlig bild av minnesplatserna.

Kopiera på Skriv

Kopiera på Skriv är en optimeringsteknik som hjälper till att öka prestanda vid kopiering av värdetyper.

låt oss säga att vi kopierar en enda sträng eller Int eller kanske någon annan värdetyp — vi kommer inte att möta några avgörande prestandaproblem i så fall. Men hur är det när vi kopierar en rad tusentals element? Kommer det fortfarande inte att skapa några prestandaproblem? Vad händer om vi bara kopierar det och inte gör några ändringar i den kopian? Är inte det extra minne vi använde bara ett slöseri i så fall?

här kommer begreppet kopia i Skriv — vid kopiering pekar varje referens till samma minnesadress. Det är först när en av referenserna ändrar de underliggande data som Swift faktiskt kopierar den ursprungliga instansen och gör ändringen.

det vill säga om det är djup kopia eller grund kopia, kommer en ny kopia inte att skapas förrän vi gör en ändring i ett av objekten.

i ovanstående kod,

  • rad 2: en djup kopia av arr1 tilldelas arr2
  • raderna 4 och 5: arr1 och arr2 pekar fortfarande på samma minnesadress
  • rad 7: ändringar gjorda i arr2
  • raderna 9 och 10: arr1 och arr2 pekar nu på olika minnesplatser

nu vet du mer om djupa och grunda kopior och hur de beter sig i olika scenarier med olika datatyper. Du kan prova dem med din egen uppsättning exempel och se vilka resultat du får.

vidare läsning

glöm inte att läsa mina andra artiklar:

  1. allt om Codable i Swift 4
  2. allt du alltid har velat veta om meddelanden i iOS
  3. färg det med gradienter — iOS
  4. kodning för iOS 11: Hur man drar & släpp i samlingar & tabeller
  5. allt du behöver veta om dagens tillägg (Widget) i IOS 10
  6. Uicollectionviewcell val gjort enkelt..!!

Lämna ett svar

Din e-postadress kommer inte publiceras.