de Payal Gupta

copierea unui obiect a fost întotdeauna o parte esențială în paradigma de codificare. Fie că este vorba în Swift, Objective-C, JAVA sau orice altă limbă, va trebui întotdeauna să copiem un obiect pentru utilizare în contexte diferite.

în acest articol, vom discuta în detaliu cum să copiați diferite tipuri de date în Swift și cum se comportă în circumstanțe diferite.

tipuri de valori și de referință

toate tipurile de date din Swift se încadrează în general în două categorii, și anume tipuri de valori și tipuri de referință.

  • Tip valoare — fiecare instanță păstrează o copie unică a datelor sale. Tipurile de date care se încadrează în această categorie includ — all the basic data types, struct, enum, array, tuples.
  • tip de referință — instanțele partajează o singură copie a datelor, iar tipul este de obicei definit ca class.

caracteristica cea mai distinctivă a ambelor tipuri constă în comportamentul lor de copiere.

ce este copia profundă și superficială?

o instanță, fie că este vorba de un tip de valoare sau de un tip de referință, poate fi copiată într — unul din următoarele moduri:

copie profundă-duplică tot

  • cu o copie profundă, orice obiect indicat de sursă este copiat și copia este indicat de destinație. Deci, vor fi create două obiecte complet separate.
  • Collections-o copie profundă a unei colecții este de două colecții cu toate elementele din colecția originală duplicat.
  • mai puțin predispuse la condițiile de rasă și funcționează bine într — un mediu multithreaded-modificări într-un obiect va avea nici un efect asupra unui alt obiect.
  • tipurile de valori sunt copiate profund.

în codul de mai sus,

  • linia 1: arr1 — matrice (un tip de valoare) de șiruri
  • Linia 2: arr1este atribuită arr2. Aceasta va crea o copie profundă a arr1 și apoi va atribui acea copie arr2
  • liniilor 7-11: orice modificări efectuate în arr2 nu se reflectă în arr1.

aceasta este copia profundă — instanțe complet separate. Același concept funcționează cu toate tipurile de valori.

în unele scenarii, adică atunci când un tip de valoare conține tipuri de referință imbricate, deep copy dezvăluie un alt tip de comportament. Vom vedea asta în secțiunile viitoare.

copie superficială — duplicate cât mai puțin posibil

  • cu o copie superficială, orice obiect indicat de sursă este indicat și de destinație. Deci, un singur obiect va fi creat în memorie.
  • Colecții-o copie superficială a unei colecții este o copie a structurii colecției, nu a elementelor. Cu o copie superficială, două colecții împărtășesc acum elementele individuale.
  • mai rapid — numai referința este copiată.
  • copierea tipurilor de referință creează o copie superficială.

în codul de mai sus,

  • liniile 1 la 8:Address tip de clasă
  • linia 10: a1 — o instanță de tipAddress
  • linia 11: a1este atribuită a2. Aceasta va crea o copie superficială a a1 și apoi va atribui acea copie a2, adică numai referința este copiată în a2.
  • liniile 16-19: orice modificare făcută în a2 se va reflecta cu siguranță în a1.

în ilustrația de mai sus, putem vedea că atât a1, cât și a2 indică aceeași adresă de memorie.

copierea tipuri de referință profund

ca de acum, știm că ori de câte ori vom încerca să copiați un tip de referință, numai referința la obiectul este copiat. Nu este creat niciun obiect nou. Ce se întâmplă dacă vrem să creăm un obiect complet separat?

putem crea o copie profundă a tipului de referință folosind metoda copy(). Conform documentației,

copy() — returnează obiectul returnat de copy(with:).

aceasta este o metodă de conveniență pentru clasele care adoptă protocolul NSCopying. Se ridică o excepție dacă nu există nicio implementare pentru copy(with:).

să restructurăm Address class pe care l-am creat în fragmentul de cod 2 pentru a se conforma protocolului NSCopying.

în codul de mai sus,

  • liniile de la 1 la 14: Address tipul clasei este conform cu NSCopying și implementează metoda copy(with:)
  • linia 16: a1 — o instanță de tip Address
  • linia 17: a1 este atribuit a2 folosind metoda copy(). Aceasta va crea o copie profundă a a1 și apoi va atribui acea copie a2, adică va fi creat un obiect complet nou.
  • liniile 22-25: orice modificare efectuată în a2 nu se va reflecta în a1 .

după cum reiese din ilustrația de mai sus, atât a1, cât și a2 indică diferite locații de memorie.

să ne uităm la un alt exemplu. De data aceasta vom vedea cum funcționează cu tipurile de referință imbricate — un tip de referință care conține un alt tip de referință.

în codul de mai sus,

  • linia 22: o copie profundă a p1 este atribuită p2 folosind metoda copy(). Aceasta implică faptul că orice modificare a uneia dintre ele nu trebuie să aibă niciun efect asupra celeilalte.
  • liniile 27 la 28: p2's name și city valorile sunt modificate. Acestea nu trebuie să reflecte în p1.
  • linia 30: p1's nameeste așa cum era de așteptat, dar este city? Ar trebui să fie "Mumbai" nu ar trebui? Dar nu putem vedea că se întâmplă. "Bangalore" a fost doar pentru p2 corect? Da … exact.?

copie profundă…!? Asta nu era de așteptat de la tine. Ai spus că vei copia totul. Și acum te comporți așa. De ce oh de ce..?! Ce fac acum?

nu intrați în panică. Să ne uităm la ce adrese de memorie are de spus în acest sens.

din ilustrația de mai sus, putem vedea că

  • p1 și p2 indicați spre diferite locații de memorie, așa cum era de așteptat.
  • dar variabilele lor address încă indică aceeași locație. Aceasta înseamnă că, chiar și după copierea lor profundă, numai referințele sunt copiate — adică o copie superficială, desigur.

vă rugăm să rețineți: de fiecare dată când copiem un tip de referință, o copie superficială este creată în mod implicit până când specificăm în mod explicit că ar trebui copiată profund.

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

în metoda de mai sus am implementat mai devreme pentru clasa Person, am creat o nouă instanță prin copierea adresei cu self.address. Aceasta va copia numai referința la obiectul adresei. Acesta este motivul pentru care atât p1, cât și p2's address indică aceeași locație.

deci, copierea obiectului folosind metoda copy() nu va crea o copie profundă adevărată a obiectului.

pentru a duplica complet un obiect de referință: tipul de referință împreună cu toate tipurile de referință imbricate trebuie copiate cu metoda copy().

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

folosind codul de mai sus în func copy(with zone: NSZone? = nil) -> orice metodă va face totul să funcționeze. Puteți vedea asta din ilustrația de mai jos.

true Deep Copy-referință și tipuri de valori

am văzut deja cum putem crea o copie profundă a tipurilor de referință. Desigur, putem face asta cu toate tipurile de referință imbricate.

dar cum rămâne cu tipul de referință imbricat într-un tip de valoare, adică o serie de obiecte sau o variabilă de tip de referință într-o structură sau poate un tuplu? Putem rezolva și asta folosind copy()? Nu, nu putem, de fapt. Metoda copy() necesită implementarea protocolului NSCopying care funcționează numai pentru subclasele NSObject. Tipurile de valori nu acceptă moștenirea, deci nu putem folosi copy() cu ele.

în linia 2, numai structura arr1 este copiată profund, dar obiectele Address din interiorul acesteia sunt încă copiate superficial. Puteți vedea asta din harta de memorie de mai jos.

elementele din ambele arr1 și arr2 ambele indică aceleași locații de memorie. Acest lucru se datorează aceluiași motiv — tipurile de referință sunt copiate superficial în mod implicit.

serializarea și apoi deserializarea unui obiect creează întotdeauna un obiect nou. Este valabil atât pentru tipurile de valori, cât și pentru tipurile de referință.

iată câteva API-uri pe care le putem folosi pentru serializarea și deserializarea datelor:

  1. NSCoding-un protocol care permite unui obiect să fie codificat și decodat pentru arhivare și distribuție. Acesta va funcționa numai cu obiecte de tip class, deoarece necesită moștenirea de la NSObject.
  2. Codabil — Faceți tipurile de date codificabile și decodificabile pentru compatibilitate cu reprezentări externe, cum ar fi JSON. Va funcționa atât pentru tipurile de valori — struct, array, tuple, basic data types, cât și pentru tipurile de referință — class.

să restructurăm clasa Address un pic mai departe pentru a se conforma protocolului Codable și să eliminăm tot codul NSCopying pe care l-am adăugat mai devreme în fragmentul de cod 3.

în codul de mai sus, liniile 11-13 vor crea o copie profundă adevărată a arr1. Mai jos este ilustrația care oferă o imagine clară a locațiilor de memorie.

Copiere pe scriere

Copiere pe scriere este o tehnică de optimizare care ajută la creșterea performanței la copierea tipurilor de valori.

să presupunem că copiem un singur șir sau Int sau poate orice alt tip de valoare — nu ne vom confrunta cu probleme cruciale de performanță în acest caz. Dar ce se întâmplă atunci când copiem o serie de mii de elemente? Nu va crea încă probleme de performanță? Ce-ar fi să-l copiem și să nu modificăm copia? Nu este acea memorie suplimentară pe care am folosit-o doar o pierdere în acest caz?

aici vine conceptul de copiere în scriere — la copiere, fiecare referință indică aceeași adresă de memorie. Numai atunci când una dintre referințe modifică datele subiacente, Swift copiază de fapt instanța originală și face modificarea.

adică, indiferent dacă este o copie profundă sau o copie superficială, o nouă copie nu va fi creată până când nu vom face o modificare a unuia dintre obiecte.

în codul de mai sus,

  • Linia 2: o copie profundă a arr1 este atribuită arr2
  • liniilor 4 și 5: arr1 și arr2 încă indică aceeași adresă de memorie
  • Linia 7: modificări făcute în arr2
  • liniile 9 și 10: arr1 și arr2 acum indică locații de memorie diferite

acum știți mai multe despre copii profunde și superficiale și modul în care se comportă în diferite scenarii cu diferite tipuri de date. Le puteți încerca cu propriul set de exemple și puteți vedea ce rezultate obțineți.

lecturi suplimentare

nu uitați să citiți celelalte articole ale mele:

  1. totul despre Codabil în Swift 4
  2. tot ce ați dorit întotdeauna să știți despre notificările din iOS
  3. colorați — l cu gradienți-iOS
  4. codificare pentru iOS 11: Cum să trageți & picătură în colecții & tabele
  5. tot ce trebuie să știți despre extensiile de astăzi (Widget) în iOS 10
  6. selecția Uicollectionviewcell ușor de făcut..!!

Lasă un răspuns

Adresa ta de email nu va fi publicată.