Payal Gupta

kopírování objektu bylo vždy nezbytnou součástí kódovacího paradigmatu. Ať už je to v Swift, Objective-C, JAVA nebo jiném jazyce, vždy budeme muset zkopírovat objekt pro použití v různých kontextech.

v tomto článku budeme podrobně diskutovat o tom, jak kopírovat různé typy dat v Swift a jak se chovají za různých okolností.

hodnotové a referenční typy

všechny datové typy v Swift obecně spadají do dvou kategorií, jmenovitě hodnotových typů a referenčních typů.

  • typ hodnoty-každá instance uchovává jedinečnou kopii svých dat. Datové typy, které spadají do této kategorie, zahrnují – all the basic data types, struct, enum, array, tuples.
  • referenční typ-instance sdílejí jednu kopii dat a typ je obvykle definován jako class.

nejvýraznějším znakem obou typů je jejich kopírovací chování.

co je hluboká a mělká kopie?

instanci, ať už jde o typ hodnoty nebo referenční typ, lze zkopírovat jedním z následujících způsobů:

Hluboká kopie-duplikuje vše

  • s hlubokou kopií se zkopíruje jakýkoli objekt, na který odkazuje zdroj, a kopie je zaměřena na cíl. Vytvoří se tedy dva zcela oddělené objekty.
  • sbírky-hluboká kopie sbírky jsou dvě sbírky se všemi prvky v původní sbírce duplikovány.
  • méně náchylné k závodním podmínkám a funguje dobře v prostředí s více vlákny-změny v jednom objektu nebudou mít žádný vliv na jiný objekt.
  • typy hodnot jsou kopírovány hluboce.

ve výše uvedeném kódu,

  • řádek 1: arr1 – pole (typ hodnoty) řetězců
  • Řádek 2: arr1 je přiřazen arr2. Tím se vytvoří hluboká kopie arr1 a poté se tato kopie přiřadí arr2
  • řádkům 7 až 11: jakékoli změny provedené v arr2 se neodrážejí v arr1 .

to je hluboká kopie-zcela oddělené instance. Stejný koncept pracuje se všemi typy hodnot.

v některých scénářích, to znamená, že typ hodnoty obsahuje vnořené referenční typy, deep copy odhaluje jiný druh chování. To uvidíme v nadcházejících sekcích.

mělká kopie-duplikáty co nejméně

  • s mělkou kopií je jakýkoli objekt, na který odkazuje zdroj, také označen cílem. Takže v paměti bude vytvořen pouze jeden objekt.
  • sbírky-mělká kopie sbírky je kopií struktury sbírky, nikoli prvků. S mělkou kopií nyní dvě sbírky sdílejí jednotlivé prvky.
  • rychlejší-zkopíruje se pouze odkaz.
  • kopírování referenčních typů vytváří mělkou kopii.

ve výše uvedeném kódu,

  • řádky 1 až 8: Address typ třídy
  • řádek 10: a1 – instance Address Typ
  • řádek 11: a1 je přiřazen a2. Tím se vytvoří mělká kopie a1 a poté se tato kopie přiřadí a2, to znamená, že pouze odkaz je zkopírován do a2.
  • řádky 16 až 19: jakékoli změny provedené v a2 se jistě projeví v a1 .

na výše uvedeném obrázku vidíme, že jak a1, tak a2 ukazují na stejnou adresu paměti.

kopírování referenčních typů hluboce

od této chvíle víme, že kdykoli se pokusíme zkopírovat referenční typ, zkopíruje se pouze odkaz na objekt. Není vytvořen žádný nový objekt. Co když chceme vytvořit zcela samostatný objekt?

můžeme vytvořit hlubokou kopii referenčního typu pomocí metody copy(). Podle dokumentace

copy (—- vrátí objekt vrácený copy(with:).

Toto je metoda pohodlí pro třídy, které přijmou protokol NSCopying. Výjimka je vznesena, pokud neexistuje žádná implementace pro copy(with:).

pojďme restrukturalizovat Address class, který jsme vytvořili v kódu Snippet 2, aby odpovídal protokolu NSCopying.

ve výše uvedeném kódu,

  • řádky 1 až 14: Address typ třídy odpovídá NSCopying a implementuje copy(with:) metodu
  • řádek 16: a1 – instance Address Typ
  • řádek 17: a1 je přiřazen a2 metodou copy(). Tím se vytvoří hluboká kopie a1 a pak přiřadit tuto kopii a2, to je zcela nový objekt bude vytvořen.
  • řádky 22 až 25: jakékoli změny provedené v a2 se nebudou odrážet v a1 .

jak je zřejmé z výše uvedeného obrázku, jak a1, tak a2 ukazují na různá místa paměti.

podívejme se na jiný příklad. Tentokrát uvidíme, jak to funguje s vnořenými referenčními typy-referenčním typem obsahujícím jiný referenční typ.

ve výše uvedeném kódu,

  • řádek 22: hluboká kopie p1 je přiřazena p2 metodou copy(). To znamená, že jakákoli změna v jedné z nich nesmí mít žádný vliv na druhou.
  • řádky 27 až 28: p2's name a city hodnoty se mění. Nesmí se odrážet v p1.
  • řádek 30: p1's name je podle očekávání, ale jeho city? Mělo by to být "Mumbai", že? Ale to nevidíme. "Bangalore" byl pouze pro p2 že? Jo … přesně.?

Hluboká kopie…!? To se od tebe nečekalo. Říkal jsi, že všechno zkopíruješ. A teď se chováš takhle. Proč oh proč..?! Co mám teď dělat? ☠

nepropadejte panice. Podívejme se na to, co v tom říkají paměťové adresy.

z výše uvedeného obrázku můžeme vidět, že

  • p1 a p2 ukazují na různá místa paměti podle očekávání.
  • ale jejich address proměnné stále ukazují na stejné místo. To znamená, že i po jejich hlubokém kopírování se zkopírují pouze odkazy — To je samozřejmě mělká kopie.

upozorňujeme: pokaždé, když zkopírujeme referenční typ, je ve výchozím nastavení vytvořena mělká kopie, dokud explicitně neurčíme, že by měla být kopírována hluboce.

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

ve výše uvedené metodě, kterou jsme dříve implementovali pro třídu Person, jsme vytvořili novou instanci zkopírováním adresy pomocí self.address . Tím se zkopíruje pouze odkaz na objekt adresy. To je důvod, proč jak p1, tak p2's address ukazují na stejné místo.

takže kopírování objektu pomocí metody copy() nevytvoří skutečnou hlubokou kopii objektu.

Chcete-li zcela duplikovat referenční objekt: referenční typ spolu se všemi vnořenými referenčními typy musí být zkopírován metodou copy().

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

pomocí výše uvedeného kódu v func copy(with zone: NSZone? = nil) -> bude vše fungovat. Můžete to vidět z níže uvedeného obrázku.

True Deep Copy-referenční a hodnotové typy

už jsme viděli, jak můžeme vytvořit hlubokou kopii referenčních typů. Samozřejmě to můžeme udělat se všemi vnořenými typy referencí.

ale co vnořený referenční typ v typu hodnoty, to je pole objektů nebo proměnná referenčního typu ve struktuře nebo možná n-tice? Můžeme to vyřešit také pomocí copy()? Ne, vlastně nemůžeme. Metoda copy() vyžaduje implementaci protokolu NSCopying, který funguje pouze pro podtřídy NSObject. Typy hodnot nepodporují dědičnost, takže s nimi nemůžeme použít copy().

v řádku 2 je pouze struktura arr1 hluboce zkopírována, ale Address objekty uvnitř jsou stále mělké kopírovány. Můžete to vidět z níže uvedené paměťové mapy.

prvky v obou arr1 a arr2 ukazují na stejná paměťová místa. Je to ze stejného důvodu-referenční typy jsou ve výchozím nastavení mělké kopírovány.

serializace a následná de-serializace objektu vždy vytvoří zcela nový objekt. Platí jak pro typy hodnot, tak pro referenční typy.

zde jsou některé API, které můžeme použít k serializaci a de-serializaci dat:

  1. NSCoding-protokol, který umožňuje kódovat a dekódovat objekt pro archivaci a distribuci. Bude pracovat pouze s objekty typu class, protože vyžaduje zdědění od NSObject .
  2. Codable-aby vaše datové typy kódovatelné a dekódovatelné pro kompatibilitu s externími reprezentacemi, jako JSON. Bude fungovat pro oba typy hodnot – struct, array, tuple, basic data typesi referenční typy – class .

pojďme restrukturalizovat třídu Address o něco dále, abychom se přizpůsobili protokolu Codable a odstranili veškerý kód NSCopying, který jsme přidali dříve v kódu Snippet 3.

ve výše uvedeném kódu vytvoří řádky 11-13 skutečnou hlubokou kopii arr1. Níže je obrázek, který poskytuje jasný obraz o místech paměti.

Copy on Write

Copy on write je optimalizační technika, která pomáhá zvýšit výkon při kopírování typů hodnot.

řekněme, že zkopírujeme jeden řetězec nebo Int nebo možná jakýkoli jiný typ hodnoty — v tomto případě nebudeme čelit žádným zásadním problémům s výkonem. Ale co když zkopírujeme pole tisíců prvků? Nebude to stále vytvářet žádné problémy s výkonem? Co když to prostě zkopírujeme a neuděláme v té kopii žádné změny? Není ta paměť navíc, kterou jsme v tom případě použili, jen plýtváním?

zde přichází koncept kopírování v zápisu-při kopírování každý odkaz ukazuje na stejnou adresu paměti. Pouze v případě, že jeden z odkazů upraví základní data, Swift skutečně zkopíruje původní instanci a provede změnu.

to znamená, že ať už je to hluboká kopie nebo mělká kopie, nová kopie nebude vytvořena, dokud neprovedeme změnu v jednom z objektů.

ve výše uvedeném kódu,

  • Řádek 2: hluboká kopie arr1 je přiřazena arr2
  • řádkům 4 a 5: arr1 a arr2 stále ukazují na stejnou adresu paměti
  • řádek 7: změny provedené v arr2
  • řádky 9 a 10: arr1 a arr2 nyní ukazují na různá umístění paměti

nyní víte více o hlubokých a mělkých kopiích a o tom, jak se chovají v různých scénářích s různými typy dat. Můžete je vyzkoušet pomocí vlastní sady příkladů a zjistit, jaké výsledky získáte.

další čtení

nezapomeňte si přečíst mé další články:

  1. vše o Codable v Swift 4
  2. vše, co jste vždy chtěli vědět o oznámeních v iOS
  3. Obarvte jej přechody-iOS
  4. kódování pro iOS 11: jak přetáhnout & drop do sbírek & tabulky
  5. vše, co potřebujete vědět o dnešních rozšířeních (Widget) v iOS 10
  6. výběr UICollectionViewCell je snadný..!!

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.