Velkommen til del fem av denne serien på Objective-C. I dag skal vi se på minnehåndtering, et element av Objective-C (og mange andre språk) som har en tendens til å gå opp på nye programmerere. De fleste skriptspråkene (som PHP) tar vare på minnehåndtering automatisk, men Objective-C krever at vi er forsiktige med vår bruk av minne og manuelt oppretter og slipper plass til våre objekter.
Det er god praksis å holde oversikt over hvor mye minne søknaden din bruker, slik at du ikke støter på lekkasjer eller hogger opp minnet på systemet. Det er enda viktigere på mobile systemer som iPhone der minnet er mye mer begrenset enn på en stasjonær maskin.
I Mål-C er det to metoder for å administrere minne, det første hvis referanse telling og det andre er søppelinnsamling. Du kan tenke på dem som manuell og automatisk, da referanse telling er kode lagt til av programmøren og søppel samling er systemet automatisk administrerer vårt minne. Et viktig notat er at søppelsamlingen ikke fungerer på iPhone, og derfor ser vi ikke hvordan det fungerer. Hvis du skulle ønske å programmere for Mac, så er det verdt å se på Apples dokumentasjon for å se hvordan søppelsamling fungerer.
Så, hvordan klarer vi vårt minne i våre apper? Først av alt, når bruker vi minne i koden vår? Når du oppretter en forekomst av en klasse (et objekt), blir minne tildelt og vårt objekt kan nå fungere riktig. Nå ser det ikke ut til at et lite objekt ser så bra ut av en avtale, men når appene dine vokser i størrelse, blir det raskt et enormt problem.
La oss se på et eksempel, si at vi har en slags tegneapp, og hver form brukeren trekker er en egen gjenstand. Hvis brukeren har tegnet 100 figurer, har vi 100 objekter satt i minnet. La oss nå si at brukeren starter over og fjerner skjermen, og trekker ytterligere 100 objekter. Hvis vi ikke klarer vårt minne riktig, kommer vi til å ende opp med 200 objekter som ikke gjør noe mer enn hogging minne.
Vi motvirker dette ved hjelp av referanse telling. Når vi oppretter et nytt objekt og bruker allokering, har objektene våre en beholdningstall på 1. Hvis vi kalder behold på objektet, er beholdtellingen nå 2 og så videre. Hvis vi slipper objektet, reduseres beholdningstallet tilbake til 1. Selv om beholdningstallet er null, vil objektet holde seg fast, men når beholdningsverdien treffer null, deallokerer systemet vårt objekt - frigjøre minnet.
Det finnes ulike metoder du kan ringe som vil ha noen effekt på minnehåndtering. Først av alt, når du oppretter et objekt ved hjelp av et metodenavn som inneholder allokering, ny eller kopi, tar du eierskap av objektet. Dette gjelder også hvis du bruker beholdningsmetoden på et objekt. Når du slipper ut, eller autorelease (mer om det senere) et objekt, tar du ikke lenger eierskap av objektet eller bryr deg hva som skjer med det.
Så, hvis vi allokerer et objekt som det;
myCarClass * bil = [myCarClass allokering];
Vi er nå ansvarlige for objektbilen, og vi må manuelt frigjøre den senere (eller autorelease den). Det er viktig å merke seg at hvis du skulle prøve å manuelt slippe et objekt som er satt til autorelease, ville programmet krasje.
Siden vi opprettet vårt objekt ved hjelp av allokering, har vårt bilobjekt nå en beholdningstall på 1, noe som betyr at det ikke vil bli fordelt. Hvis skulle også beholde vårt objekt som det;
[bærevogn];
Da er vår beholdningstall nå 2. Så for å bli kvitt objektet må vi slippe to ganger for å sette beholdningstallet til 0. Siden beholdningstellingen er nå null, blir objektet allokert.
Når du har opprettet et nytt XCode-prosjekt, har du kanskje lagt merke til noen kode som vises som standard som skaper et autorelease-basseng, frem til nå har du ignorert det - nå skal vi se hva det gjør og hvor du skal bruke det.
Koden du er kjent med å se nå, skal se slik ut;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [bassengdrenering];
Merk: Hvis du henviser til eldre dokumentasjon, ser du kanskje den siste linjen som utgivelse, snarere enn drenering, dette er et nyere tillegg til språket, men egentlig gjør det samme.
Nå skal du kunne fortelle noe om hva koden ovenfor gjør; det oppretter en forekomst av NSAutoReleasePool kalt pool, tildeler minne for det og deretter initierer det ved hjelp av init-metoden.
Når vi sender autorelease-meldingen til en gjenstand, legges det objektet til det indre, mest automatiske utgivelsesbassenget (innerst, fordi bassenger kan nestes i hverandre - mer om det senere). Når bassenget sendes dreneringsmeldingen, blir alle objektene som sendes autorelease-meldingen slettet, i hovedsak er autorelease utsatt frigjøringen til senere.
Dette er nyttig fordi mange metoder som returnerer et objekt, returnerer vanligvis et autorelatert objekt, noe som betyr at vi ikke trenger å bekymre oss om beholdningen av objektet vi nettopp har gitt, og vi må heller ikke slippe det, som det vil være gjort senere.
Jeg snakket kort før om muligheten til å neste autoriserte bassenger, men hvilken bruk er det for oss? Selv om det er flere bruksområder, er en av de vanligste bruksområdene å nest et autorelease-basseng inne i en løkke som bruker midlertidige objekter.
Hvis du for eksempel har en sløyfe som lager to midlertidige objekter for å gjøre hva det er du ønsker, hvis du setter disse to objektene til autorelease, kan du bruke dem til bassenget er sendt dreneringsmeldingen og ikke trenger å bekymre deg for å slippe manuelt å deallocate. Apple har et godt eksempel på når du vil bruke denne typen nestet autorelease-basseng i dokumentasjonen deres;
void main () NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray * args = [[NSProcessInfo processInfo] argumenter]; for (NSString * filnavn i args) NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init]; NSError * error = nil; NSString * fileContents = [[[NSString alloc] initWithContentsOfFile: filnavnkoding: NSUTF8StringEncoding error: & error] autorelease]; / * Behandle strengen, skape og autoreleasing flere objekter. * / [loopPool drain]; / * Gjør hva som helst opprydding er nødvendig. * / [bassengvann]; utgang (EXIT_SUCCESS);
Kilde: mmAutoreleasePools
Eksemplet ovenfor har litt mer enn vi trenger, men strukturen er der. Som du kan se, når programmet åpnes og hovedbelastningen er lastet, opprettes et autorelease-basseng som kalles basseng. Betyr hva som er autorisert før bassenget blir sendt, vil dreneringsmeldingen bli tildelt dette autorelease-bassenget, med mindre det er inne i et autorelease-basseng inne i dette (beklager hvis det høres litt forvirrende).
Inne i løkken, er et annet autorelease-basseng opprettet kalt loopPool. Dette bassenget er drenert inne i sløyfen, slik at alt som er frigjort i løkken frigjøres før sløyfen lukter (eller ender).
Det indre autorelease-bassenget har absolutt ingen effekt på det ytre autorelease-bassenget, du kan hekke så mange autorelease-bassenger som du trenger. Hvis vi brukte autorelease i løkken ovenfor, men ikke hadde et separat autorelease-basseng, så ville alle gjenstandene vi opprettet ikke slippes til slutten av hovedet. Så hvis løkken løp 100 ganger, ville vi ha 100 gjenstander hogging opp minne som ennå ikke er utgitt - oppblåst vår søknad.
Før vi pakker opp, la oss se på noe som kan bidra til å gjøre minnehåndtering et enklere å svelge kapittel. Så langt når vi har opprettet objekter, har vi husket hvor mange referanser et objekt har og så videre - men vi har aldri sett et faktisk tall. For utdanningsformål er det en metode vi kan bruke til å se hvor mange referanser et objekt har kalt retainCount. Måten vi skriver ut en retainCount for et objekt er som det;
NSLog (@ "keepCount for car:% d", [car retainCount]);
retainCount returnerer et heltall, så vi bruker% d for å vise det i konsollen. Det er sjeldne tilfeller (som vi ikke vil gå inn i) hvor beholdningskonfigurasjon kan være feil og som sådan ikke bør være 100% avhengig av programatisk. Den er bare implementert for feilsøking, så en app bør aldri gå live ved hjelp av retainCount-metoden.
Minnehåndtering er et emne som mange nye programmerere finner vanskelige, spesielt programmerere som kommer fra språk som tar vare på alt for deg. Vi har dekket grunnleggende, som skal være nok til at du kan finne føttene dine og begynne å integrere minnehåndtering i appene dine.
Apple har et fantastisk utviklingsdokumentasjonsbibliotek som er tilgjengelig på utviklerens nettsted, som jeg anbefaler deg å sjekke ut om du er uklart på noe vi berørt på i dag. Vi har forsøkt å holde opplæringen kort og laserfokusert i dag for å hjelpe deg med å forstå minnehåndtering uten å legge til noe annet.
Spørsmål er velkomne, som vanlig.
Du må bare eksperimentere med konsollen ved å lage en enkel gjenstand som inneholder noen syntetiserte variabler, lage noen få eksempler på denne klassen, og kontroller deretter beholdningen med retainCount-metoden. Den beste måten å forstå minnehåndtering på er å brenne opp XCode og spille rundt med allokering og beholdning osv. Husk at krasjer og feil ikke er en murvegg som de til slutt vil hjelpe deg med å unngå feilene i fremtiden.
I neste avdrag vil vi se på kategorier, en flott funksjon som er tilgjengelig i Objective-C, som kan spare utviklere mye tid og gjøre for mer forenklet kode.