í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: arr1karakterláncok tömbje (értéktípusa) arr2 – hez van rendelve. Ez létrehozza a arr1 mély másolatát, majd hozzárendeli azt a példányt a arr2
  • 7-11 .sorhoz: a arr2 – ben végrehajtott változtatások nem tükröződnek a arr1 – 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 — a Address típus
  • 11. sor: a1példánya a a2 – hez van rendelve. Ez létrehoz egy sekély másolatot a a1 – ről , majd hozzárendeli azt a példányt a a2 – hez, vagyis csak a hivatkozás másolódik a a2 – be.
  • 16-19. sor: a a2 – ben végrehajtott változtatások minden bizonnyal a a1 – 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 a NSCopying – nek és megvalósítja a copy(with:) módszert
  • 16. sor: a1 – a Address típus példánya
  • 17. sor: A a1 a a2 – hez van hozzárendelve a copy() módszerrel. Ez létrehoz egy mély másolatot a a1 – ről, majd hozzárendeli azt a példányt a a2 – 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 a a1 – 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át p2 – hoz rendeljük a copy() 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 és city értékek módosulnak. Ezek nem tükröződhetnek p1 – ben.
  • 30. sor: p1's namea várt módon, de a city? Meg kell lennie "Mumbai" nem igaz? De nem látjuk, hogy ez megtörténne. "Bangalore" csak p2 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 és p2 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:

  1. 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ökli NSObject.
  2. 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 a arr2
  • 4. és 5. sorhoz rendelik: arr1 és arr2 még mindig ugyanarra a memóriacímre mutatnak
  • 7.sor: a arr2
  • 9. és 10. sorokban végrehajtott módosítások: arr1 és arr2 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:

  1. minden, ami a kódolható Swift 4
  2. minden, amit mindig is akartam tudni értesítések iOS
  3. színátmenetek — iOS
  4. kódolás iOS 11: hogyan húzza &csepp gyűjtemények & táblázatok
  5. minden, amit tudni kell a mai kiterjesztések (Widget) az iOS 10
  6. uicollectionviewcell kiválasztása egyszerű..!!

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.