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 annettuarr2
. Tämä luo syvän kopionarr1
ja antaa sen sittenarr2
- riveille 7-11 :kaikki
arr2
tehdyt muutokset eivät heijastuarr1
.
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:
a1
—Address
tyyppi - linja 11:
a1
on annettua2
. Tämä luo matalaa kopiotaa1
ja antaa sitten kyseisen kopion arvoksia2
, eli vain viittaus kopioidaan muotoona2
. - linjat 16-19: kaikki
a2
tehdyt muutokset heijastuvat varmastia1
.

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 vastaaNSCopying
ja toteuttaacopy(with:)
menetelmä - linja 16:
a1
— esiintymäAddress
tyyppi - linja 17:
a1
on annettua2
käyttäencopy()
– menetelmää. Tämä luo syvän kopiona1
: stä ja antaa sitten kyseisen kopiona2
: lle, jolloin luodaan täysin uusi objekti. - rivit 22-25: alueella
a2
tehdyt muutokset eivät näy kohdassaa1
.

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 annettup2
: llecopy()
– 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
jacity
arvot muuttuvat. Nämä eivät saa näkyä arvossap1
. - rivi 30:
p1's
name
on odotusten mukainen, mutta sencity
? Sen pitäisi olla"Mumbai"
eikö? Mutta emme voi nähdä niin tapahtuvan."Bangalore"
oli vainp2
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
jap2
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:
- NSCoding-protokolla, jonka avulla objekti voidaan koodata ja purkaa arkistointia ja jakelua varten. Se toimii vain
class
– tyyppisillä esineillä, koska se vaatii perimistäNSObject
. - 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 types
että 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 annettuarr2
- linjoille 4 ja 5:
arr1
jaarr2
viittaavat edelleen samaan muistiosoitteeseen - rivi 7: tehdyt muutokset
arr2
- linjat 9 ja 10:
arr1
jaarr2
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:
- kaikki koodattavissa Swift 4: ssä
- kaikki mitä olet aina halunnut tietää ilmoituksista iOS: ssä
- Color it with GRADIENTS — iOS
- koodaus iOS 11: How to drag & drop into collections & tables
- All you need to know about Today Extensions (Widget) iOS 10: ssä
- uicollectionviewcellin valinta tehty helpoksi..!!