Bine ați venit la partea a patra a acestei serii despre Obiectiv-C. Astăzi vom privi gestionarea memoriei, un element al obiectivului C (și multe alte limbi) care tinde să evite programele noi. Cele mai multe limbi de scripting (cum ar fi PHP) au grijă de gestionarea memoriei în mod automat, dar Obiectiv-C cere ca noi să fim atenți la utilizarea de memorie și să creăm și să eliberăm manual spațiu pentru obiectele noastre.
Este o bună practică să urmăriți cât de multă memorie utilizează aplicația dvs., astfel încât să nu vă confruntați cu eventuale scurgeri sau să vă faceți griji în memoria sistemului. Este și mai important pe sistemele mobile, cum ar fi iPhone, unde memoria este mult mai limitată decât pe o mașină desktop.
În Obiectiv-C există două metode pentru gestionarea memoriei, prima dacă numărătoarea de referință, iar cea de-a doua este colecția de gunoi. Vă puteți gândi la ele ca manual și automat, deoarece numărarea de referință este codul adăugat de programator și colecția de gunoi este sistemul care gestionează automat memoria noastră. O notă importantă este că colecția de gunoi nu funcționează pe iPhone, motiv pentru care nu ne vom uita la modul în care funcționează. Dacă doriți să programați pentru Mac, atunci merită să vă uitați la documentația Apple pentru a vedea cum funcționează colecția de gunoi.
Deci, cum ne gestionăm memoria în aplicațiile noastre? Mai întâi de toate, când folosim memoria în codul nostru? Când creați o instanță a unei clase (un obiect), memoria este alocată și obiectul nostru poate funcționa corect acum. Acum, un mic obiect ar putea să nu pară așa de mare, dar atunci când aplicațiile cresc în dimensiune - devine repede o problemă enormă.
Să aruncăm o privire asupra unui exemplu, să spunem că avem un fel de aplicație desenată și fiecare formă pe care utilizatorul o trage este un obiect separat. Dacă utilizatorul a desenat 100 de forme, atunci avem 100 de obiecte așezate în memorie. Acum, să spunem că utilizatorul începe de la început și șterge ecranul, apoi desenează încă 100 de obiecte. Dacă nu ne gestionăm corect memoria, vom ajunge la 200 de obiecte care nu fac altceva decât să memoreze memoria.
Ne contrazic acest lucru prin numărarea de referință. Atunci când creăm un obiect nou și folosim alocarea, obiectele noastre au un număr de reținere de 1. Dacă numim reținere pe acel obiect, numărul de retinere este acum 2 și așa mai departe. Dacă eliberăm obiectul, numărul de reținere scade înapoi la 1. În timp ce numărul de reținere este diferit de zero, obiectul nostru se va lipi, dar când numărul de rețineri atinge zero, sistemul nealocătează obiectul - eliberând memoria.
Există diferite metode pe care le puteți apela, care vor avea un efect asupra gestionării memoriei. Mai întâi, atunci când creați un obiect utilizând un nume de metodă care conține alocare, nouă sau copie, luați proprietatea asupra acelui obiect. Acest lucru este valabil și dacă utilizați metoda de reținere a unui obiect. Odată ce lansați sau autorelaționați (mai târziu despre acest lucru) un obiect, nu mai luați dreptul de proprietate asupra acelui obiect sau nu-i pasă ce se întâmplă cu acesta.
Deci, dacă alocăm un obiect ca atare;
myCarClass * car = [alocarea meaCarClass];
Suntem acum responsabili pentru mașina obiect și trebuie să o eliberăm manual mai târziu (sau să o redresăm automat). Este important să rețineți că, dacă ați încerca să lansați manual un obiect care a fost setat să se autorelează, aplicația s-ar prăbuși.
De când am creat obiectul folosind alocarea, obiectul nostru auto are acum un număr de reținere de 1, ceea ce înseamnă că acesta nu va fi dealocat. Dacă ar fi de asemenea să ne păstrăm obiectul așa;
[reținerea mașinii];
Apoi numărul nostru de retinere este acum 2. Deci, pentru a scăpa de obiect, trebuie să eliberăm de două ori pentru a seta numărul de reținere la 0. Deoarece numărul de reținere este acum zero, obiectul va fi dealocat.
Când ați creat un nou proiect XCode, este posibil să fi observat un cod care apare în mod implicit, ceea ce creează o piscină autoreleasă, până acum ați ignorat - acum vom vedea ce face și unde să o utilizați.
Codul pe care probabil că îl cunoașteți ar trebui să arate așa;
NSAutoreleasePool * pool = [[NSAutoreleasePool alocare] init]; [scurgere piscină];
Notă: dacă vă referiți la documentația mai veche, puteți vedea ultima linie ca eliberare, mai degrabă decât scurgeți, aceasta este o adăugare mai recentă a limbii, dar în esență face același lucru.
Până acum ar trebui să puteți spune într-o oarecare măsură ce face codul de mai sus; se creează o instanță a NSAutoReleasePool numită piscină, alocând memorie pentru aceasta și apoi inițializându-l folosind metoda init.
Atunci când trimitem mesajul de autorelează unui obiect, acel obiect este apoi adăugat la cea mai interioară piscină de lansare automată (cea mai mare parte din interior, deoarece piscinele pot fi imbricate între ele - mai târziu, mai târziu). Atunci când piscina este trimisă mesajul de scurgere, atunci toate obiectele trimise mesajul autorelease sunt eliberate, în esență, autorelease amână eliberarea până mai târziu.
Acest lucru este util deoarece multe metode care returnează un obiect, în mod normal, returnează un obiect autorelezat, ceea ce înseamnă că nu trebuie să ne facem griji cu privire la numărul de reținere al obiectului pe care tocmai l-am dat, și nici nu trebuie să îl eliberăm, așa cum va fi făcut mai târziu.
Am vorbit pe scurt despre abilitatea de a cuibui piscinele autorelelate, dar ce folosim noi pentru noi? Deși există mai multe utilizări, una dintre cele mai frecvente utilizări este de a cuibui o piscină autorelease într-o buclă care utilizează obiecte temporare.
De exemplu, dacă aveți o buclă care creează două obiecte temporare pentru a face ceea ce doriți, dacă setați aceste două obiecte pentru a le autorelaza, puteți să le utilizați până când piscina este trimisă mesajul de scurgere și nu trebuie să vă faceți griji cu privire la eliberarea manuală pentru a dezaloca. Apple are un exemplu minunat atunci când veți folosi acest tip de piscină autorelease imbricate în documentația lor;
void main () NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray * args = argumentele [[NSProcessInfo processInfo]]; pentru (NSString * filename în args) NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init]; NSError * eroare = zero; NSString * fileContents = [[NSString alloc] initWithContentsOfFile: codificare nume_fișier: eroare NSUTF8StringEncoding: & error] autorelease]; / * Procesează șirul, creând și autorelezând mai multe obiecte. * / [scurgere loopPool]; / * Faceți orice curățare este necesară. * / [scurgere piscină]; ieșire (EXIT_SUCCESS);
Sursa: mmAutoreleasePools
Exemplul de mai sus are un pic mai mult decât avem nevoie, dar structura este acolo. După cum puteți vedea, atunci când aplicația este deschisă și principala este încărcată, se creează o piscină autorelease numită piscină. Semnificând orice autorelează înainte ca piscina să fie trimisă, mesajul de scurgere va fi atribuit la această piscină autorelease, cu excepția cazului în care se află în interiorul unei piscine autorelease din interiorul acesteia (îmi pare rău dacă sună puțin cam confuz).
În interiorul buclei, este creată o altă piscină autorelease numită loopPool. Această piscină este drenată în interiorul bucla, astfel încât orice autoreleased în interiorul bucla este eliberat înainte de bucla iterează (sau se termină).
Piscina interioară autorelease nu are niciun efect asupra piscinei autorelease exterioare, puteți cuibări cât mai multe piscine de autorelează de care aveți nevoie. Dacă am folosit autorelease în buclă de mai sus, dar nu aveam o piscină autorelează separată, atunci toate obiectele pe care le creaam nu vor fi lansate până la sfârșitul principalului. Deci, dacă bucla a fugit de 100 de ori, am avea 100 de obiecte care scot în evidență memoria care nu a fost încă eliberată - umflarea aplicației noastre.
Înainte de a încheia, să examinăm ceva care ar putea ajuta gestionarea memoriei să fie un capitol mai ușor de înghițit. Până acum, când am creat obiecte, ne-am amintit câte referințe are un obiect și așa mai departe - dar nu am văzut niciodată un număr real. În scopul educației, există o metodă pe care o putem folosi pentru a vedea câte referințe un obiect a numit retainCount. Modul în care imprimați un reținut pentru un obiect este similar;
NSLog (@ "retainCount pentru mașină:% d", [car retainCount]);
holdCount returnează un număr întreg, deci folosim% d pentru ao afișa în consola. Există cazuri rare (la care nu vom merge), unde reținereaCount poate fi greșită și ca atare nu ar trebui să se bazeze 100% pe program. Este implementat numai pentru depanare, deci o aplicație nu ar trebui să funcționeze niciodată live folosind metoda holdCount.
Managementul memoriei este un subiect pe care mulți dintre noi programatori îl găsesc dificil, în special programatori care vin din limbi care se ocupă de toate acestea pentru tine. Am acoperit elementele de bază, care ar trebui să fie suficiente pentru a vă permite să vă găsiți picioarele și să începeți încorporarea gestionării memoriei în aplicațiile dvs..
Apple are o bibliotecă fantastică de documentare pentru dezvoltatori, disponibilă pe site-ul web al dezvoltatorului, care vă recomandăm să verificați dacă nu sunteți sigur despre ceea ce am atins astăzi. Am încercat să păstrăm tutorialul scurt și focalizat pe laser astăzi pentru a vă ajuta să înțelegeți gestionarea memoriei fără a adăuga alte pufoase.
Întrebările sunt binevenite, ca de obicei.
Pur și simplu experimentați cu consola, creând un obiect simplu care conține câteva variabile sintetizate, creați câteva exemple din această clasă și apoi verificați numărul de reținere utilizând metoda retainCount. Cea mai bună modalitate de a înțelege gestionarea memoriei este de a declanșa XCode și de a juca în jurul valorii de alocare și reținere etc, amintiți-vă că accidentele și greșelile nu sunt un zid de cărămidă, deoarece în cele din urmă vă vor ajuta să evitați greșelile viitoare.
În următoarea tranșă vom examina categoriile, o caracteristică excelentă disponibilă în Obiectiv-C, care poate salva dezvoltatorii mult timp și face pentru codul mai simplist.