írta Payal Gupta

az objektum másolása mindig is elengedhetetlen része volt a kódolási paradigmának. Legyen szó Swift – ről, Objective-C-ről, JAVA-ról vagy bármilyen más nyelvről, mindig másolni kell egy objektumot, hogy különböző kontextusokban használhassuk.
ebben a cikkben részletesen tárgyaljuk, hogyan lehet másolni a különböző adattípusokat a Swift-ben, és hogyan viselkednek különböző körülmények között.
érték-és Referenciatípusok
a Swift összes adattípusa nagyjából két kategóriába sorolható, nevezetesen az értéktípusokba és a referenciatípusokba.
- értéktípus — minden példány egyedi másolatot tart az adatairól. Az ebbe a kategóriába tartozó adattípusok a következők: —
all the basic data types, struct, enum, array, tuples
. - referenciatípus — a példányok az adatok egyetlen példányán osztoznak, és a típust általában
class
– ként definiálják.
mindkét típus legjellemzőbb jellemzője a másolási viselkedésük.
mi a mély és sekély másolat?
egy példány, függetlenül attól, hogy értéktípus vagy referenciatípus, a következő módok egyikével másolható:
mély másolás — mindent lemásol
- mély másolattal a forrás által mutatott bármely objektum másolódik, a másolatot pedig a cél. Tehát két teljesen különálló objektum jön létre.
- gyűjtemények — a gyűjtemény mély példánya két gyűjtemény, az eredeti gyűjtemény összes elemével megkettőzve.
- kevésbé hajlamos a versenyfeltételekre, és jól teljesít egy többszálú környezetben — az egyik tárgyban bekövetkező változások nem lesznek hatással egy másik tárgyra.
- az Értéktípusok mélyen másolódnak.
a fenti kódban,
- 1. sor:
arr1
– a - 2.sor:
arr1
karakterláncok tömbje (értéktípusa)arr2
– hez van rendelve. Ez létrehozza aarr1
mély másolatát, majd hozzárendeli azt a példányt aarr2
- 7-11 .sorhoz: a
arr2
– ben végrehajtott változtatások nem tükröződnek aarr1
– ban.
ez a deep copy — teljesen különálló példányok. Ugyanez a koncepció működik az összes értéktípussal.
egyes esetekben, amikor egy értéktípus beágyazott referenciatípusokat tartalmaz, a deep copy másfajta viselkedést tár fel. Ezt a következő szakaszokban fogjuk látni.
sekély másolat — a lehető legkevesebbet duplikálja
- sekély másolattal a forrás által mutatott bármely tárgyra A cél is mutat. Tehát csak egy objektum jön létre a memóriában.
- gyűjtemények — a gyűjtemény sekély példánya a gyűjtemény szerkezetének másolata, nem pedig az elemek. Sekély másolattal két gyűjtemény osztja meg az egyes elemeket.
- gyorsabb-csak a hivatkozás másolódik.
- referenciatípusok másolása sekély másolatot hoz létre.
a fenti kódban,
- 1-8. sor:
Address
osztálytípus - 10.sor:
a1
— aAddress
típus - 11. sor:
a1
példánya aa2
– hez van rendelve. Ez létrehoz egy sekély másolatot aa1
– ről , majd hozzárendeli azt a példányt aa2
– hez, vagyis csak a hivatkozás másolódik aa2
– be. - 16-19. sor: a
a2
– ben végrehajtott változtatások minden bizonnyal aa1
– ben tükröződnek .

a fenti ábrán láthatjuk, hogy mind a a1
, mind a a2
ugyanarra a memóriacímre mutat.
Referenciatípusok másolása mélyen
mostantól tudjuk, hogy amikor megpróbálunk másolni egy referenciatípust, csak az objektumra mutató hivatkozás kerül másolásra. Nem jön létre új objektum. Mi van, ha egy teljesen különálló objektumot akarunk létrehozni?
létrehozhatunk egy mély másolatot a referenciatípusról a copy()
módszerrel. A dokumentáció szerint
copy() — visszaadja a copy(with:)
által visszaadott objektumot.
ez egy kényelmi módszer azoknak az osztályoknak, amelyek elfogadják a NSCopying
protokollt. Kivételt képez, ha a copy(with:)
esetében nincs megvalósítás.
alakítsuk át a Address class
– et, amelyet a 2.kódrészletben hoztunk létre, hogy megfeleljen a NSCopying
protokollnak.
a fenti kódban,
- 1-14. sor:
Address
osztálytípus megfelel aNSCopying
– nek és megvalósítja acopy(with:)
módszert - 16. sor:
a1
– aAddress
típus példánya - 17. sor: A
a1
aa2
– hez van hozzárendelve acopy()
módszerrel. Ez létrehoz egy mély másolatot aa1
– ről, majd hozzárendeli azt a példányt aa2
– hez, vagyis egy teljesen új objektum jön létre. - 22-25 .sor: a
a2
– ben végrehajtott változtatások nem tükröződnek aa1
– ben.

amint az a fenti ábrán látható, mind a a1
, mind a a2
különböző memóriahelyekre mutat.
nézzünk egy másik példát. Ezúttal meglátjuk, hogyan működik a beágyazott referenciatípusokkal — egy másik referenciatípust tartalmazó referenciatípussal.
a fenti kódban,
- 22. sor: a
p1
mély másolatátp2
– hoz rendeljük acopy()
módszerrel. Ez azt jelenti, hogy az egyikben bekövetkező bármilyen változás nem lehet hatással a másikra. - 27-28.sor:
p2's
name
éscity
értékek módosulnak. Ezek nem tükröződhetnekp1
– ben. - 30. sor:
p1's
name
a várt módon, de acity
? Meg kell lennie"Mumbai"
nem igaz? De nem látjuk, hogy ez megtörténne."Bangalore"
csakp2
volt, igaz? Igen … pontosan.?
mély másolat…!? Ezt nem vártam tőled. Azt mondtad, mindent lemásolsz. És most így viselkedsz. Miért ó, miért..?! Most mit csináljak? 6754
ne ess pánikba. Nézzük meg, mit kell mondani a memóriacímekről ebben.

a fenti illusztrációból láthatjuk, hogy
-
p1
ésp2
mutasson a várt módon különböző memóriahelyekre. - de
address
változóik még mindig ugyanarra a helyre mutatnak. Ez azt jelenti, hogy még a mély másolás után is csak a referenciákat másolják — vagyis természetesen egy sekély másolatot.
kérjük, vegye figyelembe: minden alkalommal, amikor másolunk egy referenciatípust, alapértelmezés szerint sekély másolat jön létre, amíg kifejezetten meg nem határozzuk, hogy mélyen kell másolni.
func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}
a fenti módszerben, amelyet korábban a Person
osztályra implementáltunk, létrehoztunk egy új példányt a cím self.address
– vel történő másolásával . Ez csak a címobjektumra mutató hivatkozást másolja. Ez az oka annak, hogy mind a p1
, mind a p2's
address
ugyanarra a helyre mutat.
tehát az objektum copy()
módszerrel történő másolása nem hoz létre valódi mély másolatot az objektumról.
egy referenciaobjektum teljes másolásához: a referenciatípust az összes beágyazott referenciatípussal együtt a copy()
módszerrel kell másolni.
let person = Person(self.name, self.address.copy() as? Address)
a fenti kód használata a func copy(with zone: NSZone? = nil) ->
bármely módszer mindent működni fog. Ezt az alábbi ábrán láthatja.

True Deep Copy-referencia-és Értéktípusok
már láttuk, hogyan hozhatunk létre mély másolatot a referenciatípusokról. Természetesen ezt megtehetjük az összes beágyazott referenciatípussal.
de mi a helyzet a beágyazott referenciatípussal egy értéktípusban, azaz objektumok tömbjében, vagy egy struct vagy talán egy tuple referencia típusú változójában? Meg tudjuk oldani ezt a copy()
használatával is? Nem, igazából nem. A copy()
módszer a NSCopying
protokoll megvalósítását igényli, amely csak a NSObject
alosztályok esetében működik. Az értéktípusok nem támogatják az öröklődést, ezért nem használhatjuk velük a copy()
értéket.
a 2.sorban csak a arr1
szerkezetét másolják mélyen, de a benne lévő Address
objektumok még mindig sekélyen másolódnak. Láthatjuk, hogy az alábbi memória térkép.

mind a arr1
, mind a arr2
elemek ugyanarra a memóriahelyre mutatnak. Ennek oka ugyanaz az ok — a referenciatípusok alapértelmezés szerint sekélyek.
egy objektum Sorosítása, majd sortalanítása mindig egy teljesen új objektumot hoz létre. Mind az értéktípusokra, mind a referencia típusokra érvényes.
Íme néhány API, amit az adatok sorosítására és de-serializációjára használhatunk:
- NSCoding-egy protokoll, amely lehetővé teszi egy objektum kódolását és dekódolását archiválás és terjesztés céljából. Ez csak akkor működik,
class
típusú objektumokat igényel örökliNSObject
. - kódolható — az adattípusok kódolhatóvá és dekódolhatóvá tétele a külső megjelenítésekkel, például a JSON-nal való kompatibilitás érdekében. Ez működni fog mindkét értéktípus –
struct, array, tuple, basic data types
valamint referencia típusok —class
.
alakítsuk át a Address
osztályt egy kicsit tovább, hogy megfeleljen a Codable
protokollnak, és távolítsuk el az összes NSCopying
kódot, amelyet korábban a 3.kódrészletben adtunk hozzá.
a fenti kódban a 11-13 sorok valódi mély másolatot hoznak létre arr1
. Az alábbiakban látható az ábra, amely világos képet ad a memóriahelyekről.

Copy on Write
Copy on write egy optimalizálási technika, amely segít növelni a teljesítményt, ha a másolás érték típusok.
tegyük fel, hogy egyetlen karakterláncot vagy Int — t vagy esetleg bármilyen más értéktípust másolunk-ebben az esetben nem fogunk szembesülni kritikus teljesítményproblémákkal. De mi van akkor, ha több ezer elemből álló tömböt másolunk? Még mindig nem okoz teljesítményproblémákat? Mi lenne, ha lemásolnánk, és nem változtatnánk rajta semmit? Ez az extra memória, amit használtunk, nem pazarlás ebben az esetben?
itt jön a másolás fogalma írásban — másoláskor minden referencia ugyanarra a memóriacímre mutat. Csak akkor, ha az egyik hivatkozás módosítja az alapul szolgáló adatokat, a Swift ténylegesen lemásolja az eredeti példányt, és elvégzi a módosítást.
ez azt jelenti, hogy mély vagy sekély másolat, új másolat nem jön létre, amíg nem változtatunk az egyik objektumon.
a fenti kódban,
- 2. sor: a
arr1
mély másolatát aarr2
- 4. és 5. sorhoz rendelik:
arr1
ésarr2
még mindig ugyanarra a memóriacímre mutatnak - 7.sor: a
arr2
- 9. és 10. sorokban végrehajtott módosítások:
arr1
ésarr2
most különböző memóriahelyekre mutatnak
most már többet tud a mély és sekély másolatokról, valamint arról, hogyan viselkednek különböző forgatókönyvekben, különböző adattípusokkal. Kipróbálhatja őket saját példáival, és megnézheti, milyen eredményeket kap.
további olvasmányok
ne felejtsd el elolvasni a többi cikkemet:
- minden, ami a kódolható Swift 4
- minden, amit mindig is akartam tudni értesítések iOS
- színátmenetek — iOS
- kódolás iOS 11: hogyan húzza &csepp gyűjtemények & táblázatok
- minden, amit tudni kell a mai kiterjesztések (Widget) az iOS 10
- uicollectionviewcell kiválasztása egyszerű..!!