by Payal Gupta

objektin kopiointi on aina ollut olennainen osa koodauksen paradigmaa. Olipa kyseessä Swift, Objective-C, JAVA tai mikä tahansa muu kieli, meidän täytyy aina kopioida objekti käytettäväksi eri yhteyksissä.

tässä artikkelissa käsitellään yksityiskohtaisesti, miten eri tietotyypit kopioidaan Swiftissä ja miten ne käyttäytyvät eri olosuhteissa.

arvo ja Viitetyypit

kaikki Swiftin tietotyypit jakautuvat karkeasti kahteen ryhmään: arvotyypit ja viitetyypit.

  • arvotyyppi-jokainen instanssi säilyttää yksilöllisen kopion tiedoistaan. Tähän luokkaan kuuluvia tietotyyppejä ovat — all the basic data types, struct, enum, array, tuples.
  • Viitetyyppi-instanssit jakavat yhden kopion tiedoista,ja tyyppi määritellään yleensä arvoksi class.

molempien tyyppien erottavin piirre on niiden kopiointikäyttäytyminen.

mikä on syvä ja matala kopio?

instanssi, oli se sitten arvotyyppi tai viitetyyppi, voidaan kopioida jollakin seuraavista tavoista:

Deep copy — monistaa kaiken

  • syvällä kopiolla kaikki lähteen osoittama objekti kopioidaan ja kohde osoittaa kopion. Syntyy siis kaksi täysin erillistä objektia.
  • kokoelmat-syvä kopio kokoelmasta on kaksi kokoelmaa, joissa kaikki alkuperäisen kokoelman elementit on kopioitu.
  • vähemmän altis kilpailuolosuhteille ja toimii hyvin monisäikeisessä ympäristössä — yhden esineen muutoksilla ei ole vaikutusta toiseen kohteeseen.
  • arvotyypit kopioidaan syvällisesti.

edellä mainitussa koodissa,

  • rivi 1: arr1 – merkkijonojen joukko (arvotyyppi)
  • rivi 2: arr1 on annettu arr2. Tämä luo syvän kopion arr1 ja antaa sen sitten arr2
  • riveille 7-11 :kaikki arr2 tehdyt muutokset eivät heijastu arr1.

tätä deep copy on – täysin erillisiä instansseja. Sama konsepti toimii kaikilla arvotyypeillä.

joissakin skenaarioissa, eli kun arvotyyppi sisältää sisäkkäisiä viitetyyppejä, syvä kopio paljastaa toisenlaisen käyttäytymisen. Se nähdään tulevissa jaksoissa.

Shallow copy-kaksoiskappaleita mahdollisimman vähän

  • kun on matala kopio, myös kohde osoittaa lähteen osoittaman kohteen. Muistissa syntyy siis vain yksi esine.
  • kokoelmat-matala kopio kokoelmasta on kopio kokoelmarakenteesta, ei elementeistä. Matalassa kopiossa kaksi kokoelmaa jakaa nyt yksittäiset elementit.
  • nopeammin-vain viittaus kopioidaan.
  • vertailutyyppien kopiointi luo matalan kopion.

edellä mainitussa koodissa,

  • linjat 1-8: Address luokkatyyppi
  • linja 10: a1Address tyyppi
  • linja 11: a1 on annettu a2. Tämä luo matalaa kopiota a1 ja antaa sitten kyseisen kopion arvoksi a2, eli vain viittaus kopioidaan muotoon a2.
  • linjat 16-19: kaikki a2 tehdyt muutokset heijastuvat varmasti a1 .

yllä olevasta kuvasta voidaan nähdä, että sekä a1 että a2 viittaavat samaan muistiosoitteeseen.

kopioivat Viitetyypit syvästi

tästä lähtien tiedämme, että aina kun yritämme kopioida viitetyyppiä, kopioidaan vain viittaus kohteeseen. Uutta objektia ei luoda. Entä jos haluamme luoda täysin erillisen objektin?

voimme luoda viitetyypistä syvän kopion copy() – menetelmällä. Dokumentaation mukaan

copy () – palauttaa copy(with:)palauttaman objektin.

tämä on mukavuusmenetelmä luokille, jotka ottavat käyttöön NSCopying – protokollan. Poikkeus tehdään, jos copy(with:)ei toteuteta.

uudistetaan koodinpätkässä 2 luomamme Address class vastaamaan NSCopying – protokollaa.

edellä mainitussa koodissa,

  • linjat 1-14: Address luokkatyyppi vastaa NSCopying ja toteuttaa copy(with:) menetelmä
  • linja 16: a1 — esiintymä Address tyyppi
  • linja 17: a1 on annettu a2 käyttäen copy() – menetelmää. Tämä luo syvän kopion a1: stä ja antaa sitten kyseisen kopion a2: lle, jolloin luodaan täysin uusi objekti.
  • rivit 22-25: alueella a2 tehdyt muutokset eivät näy kohdassa a1 .

kuten yllä olevasta kuvauksesta käy ilmi, sekä a1 että a2 viittaavat eri muistipaikkoihin.

katsotaanpa toista esimerkkiä. Tällä kertaa näemme, miten se toimii sisäkkäisten referenssityyppien kanssa — referenssityypin, joka sisältää toisen referenssityypin.

edellä mainitussa koodissa,

  • rivi 22: p1: N syväkopio on annettu p2: lle copy() – menetelmällä. Tämä tarkoittaa sitä, että millään toisella niistä tapahtuvalla muutoksella ei saa olla mitään vaikutusta toiseen.
  • rivit 27-28: p2's name ja city arvot muuttuvat. Nämä eivät saa näkyä arvossa p1.
  • rivi 30: p1's name on odotusten mukainen, mutta sen city? Sen pitäisi olla "Mumbai" eikö? Mutta emme voi nähdä niin tapahtuvan. "Bangalore" oli vain p2 oikea? Juuri niin.?

Deep copy…!? Sitä ei odotettu sinulta. Sanoit kopioivasi kaiken. Ja nyt käyttäydyt näin. Miksi voi miksi..?! Mitä minä nyt teen? ☠️

Don ’ t panic. Katsotaan, mitä muistiosoitteet kertovat.

yllä olevasta kuvauksesta voimme nähdä, että

  • p1 ja p2 osoita eri muistipaikoille odotetusti.
  • , mutta niiden address muuttujat viittaavat edelleen samaan paikkaan. Tämä tarkoittaa sitä, että syvällisestikin kopioimisen jälkeen kopioidaan vain viittaukset — siis tietenkin pinnallinen kopio.

huomaa: aina kun kopioimme viitetyypin, oletuksena luodaan matala kopio, kunnes määritämme, että se on kopioitava syvälle.

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

edellä mainitussa menetelmässä, jonka toteutimme aiemmin Person luokalle, olemme luoneet uuden instanssin kopioimalla osoitteen self.address. Tämä kopioi vain viittauksen osoiteobjektiin. Tästä syystä sekä p1 että p2's address viittaavat samaan paikkaan.

joten kappaleen kopioiminen copy() – menetelmällä ei luo kohteesta todellista syväkopiota.

referenssiobjektin täydellinen kopiointi: viitetyyppi ja kaikki sisäkkäiset viitetyypit on kopioitava copy() – menetelmällä.

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

käyttämällä yllä olevaa koodia func copy(with zone: NSZone? = nil) -> mikä tahansa menetelmä saa kaiken toimimaan. Sen näet oheisesta kuvasta.

True Deep Copy-Reference and Value types

olemme jo nähneet, miten voimme luoda deep-kopion viitetyypeistä. Tietenkin voimme tehdä sen kaikkien sisäkkäisten viitetyyppien kanssa.

mutta entä sisäkkäinen viitetyyppi arvotyypissä, joka on objektien joukko, tai viitetyypin muuttuja struktissa tai ehkä tuplessa? Voimmeko ratkaista tämän käyttämällä myös copy()? – Emme voi. copy() menetelmä edellyttää NSCopying protokollan toteuttamista, joka toimii vain NSObject alaluokille. Arvotyypit eivät tue perintöä, joten emme voi käyttää copy() niiden kanssa.

rivillä 2 vain arr1 rakenne on syvään kopioitu, mutta sen sisällä olevat Address esineet ovat edelleen matalia kopioituja. Sen näet alla olevasta muistikartasta.

sekä arr1 että arr2 alkuaineet viittaavat samoihin muistipaikkoihin. Tämä johtuu samasta syystä — referenssityypit ovat oletusarvoisesti pinnallisia kopioituja.

esineen sarjallistaminen ja sen jälkeen de-serialisointi luo aina uuden esineen. Se on voimassa sekä arvotyypeille että viitetyypeille.

tässä on joitakin sovellusliittymiä, joita voimme käyttää tietojen sarjallistamiseen ja de-serializointiin:

  1. NSCoding-protokolla, jonka avulla objekti voidaan koodata ja purkaa arkistointia ja jakelua varten. Se toimii vain class – tyyppisillä esineillä, koska se vaatii perimistä NSObject.
  2. koodattava-tee tietotyypeistäsi koodattavia ja dekoodattavia, jotta ne ovat yhteensopivia ulkoisten edustustojen, kuten JSONin kanssa. Se toimii sekä arvotyypeille – struct, array, tuple, basic data typesettä viitetyypeille – class .

uudistetaan Address – Luokka hieman pidemmälle Codable – protokollan mukaiseksi ja poistetaan kaikki NSCopying – koodi, jonka lisäsimme aiemmin koodinpätkässä 3.

yllä olevassa koodissa rivit 11-13 luovat todellisen syvän kopion arr1. Alla kuvituskuva, joka antaa selkeän kuvan muistipaikoista.

Copy on Write

Copy on write on optimointitekniikka, joka auttaa parantamaan suorituskykyä kopioidessaan arvotyyppejä.

sanotaan, että kopioimme yhden merkkijonon tai Int tai ehkä minkä tahansa muun arvotyypin – emme kohtaa siinä tapauksessa mitään ratkaisevia suorituskykyyn liittyviä kysymyksiä. Mutta mitä on sanottava, kun kopioimme tuhansien alkuaineiden joukon? Eikö se silti aiheuta suorituskykyyn liittyviä ongelmia? Mitä jos kopioimme sen, emmekä tee siihen muutoksia? Eikö käyttämämme ylimääräinen muisti ole turhaa?

tästä tulee kopioinnin käsite kirjoituksessa-kopioidessa jokainen viite osoittaa samaan muistiosoitteeseen. Vasta kun jokin viittauksista muuttaa taustalla olevaa tietoa, Swift itse asiassa kopioi alkuperäisen ilmentymän ja tekee muutoksen.

eli oli kyseessä sitten syvä tai matala kopio, uutta kopiota ei synny ennen kuin johonkin esineeseen tehdään muutos.

edellä mainitussa koodissa,

  • rivi 2: arr1: n syvä kopio on annettu arr2
  • linjoille 4 ja 5: arr1 ja arr2 viittaavat edelleen samaan muistiosoitteeseen
  • rivi 7: tehdyt muutokset arr2
  • linjat 9 ja 10: arr1 ja arr2 viittaavat nyt eri muistipaikkoihin

nyt tiedät enemmän syvistä ja matalista kopioista ja siitä, miten ne käyttäytyvät eri skenaarioissa eri tietotyypeillä. Voit kokeilla niitä omilla esimerkeilläsi ja katsoa, mitä tuloksia saat.

lisätietoja

älä unohda lukea muita artikkeleitani:

  1. kaikki koodattavissa Swift 4: ssä
  2. kaikki mitä olet aina halunnut tietää ilmoituksista iOS: ssä
  3. Color it with GRADIENTS — iOS
  4. koodaus iOS 11: How to drag & drop into collections & tables
  5. All you need to know about Today Extensions (Widget) iOS 10: ssä
  6. uicollectionviewcellin valinta tehty helpoksi..!!

Vastaa

Sähköpostiosoitettasi ei julkaista.