Minne må allokeres for hvert objekt som søknaden din bruker, og det må distribueres når søknaden din er ferdig med det for å sikre at programmet bruker minnet så effektivt som mulig. Det er viktig å forstå Objective-Cs minnehåndteringsmiljø for å sikre at programmet ikke lekker ut minne eller prøver å referere til objekter som ikke lenger eksisterer.
Teller referanser til et objektI motsetning til C #, gjør Objective-C ikke bruk søppelsamling. I stedet bruker det et referanse-telling miljø som sporer hvor mange steder som bruker et objekt. Så lenge det er minst en referanse til objektet, sørger objektiv-C-kjøretiden for at objektet skal ligge i minnet. Men hvis det ikke lenger er noen referanser til objektet, kan kjøretiden frigjøre objektet og bruke minnet til noe annet. Hvis du prøver å få tilgang til et objekt etter at det er utgitt, vil programmet mest sannsynlig krasje.
Det er to gjensidig eksklusive måter å håndtere objektreferanser på i Objective-C:
ARC er den foretrukne måten å håndtere minne i nye applikasjoner, men det er fortsatt viktig å forstå hva som skjer under hetten. Den første delen av dette kapitlet viser hvordan du manuelt sporer objekttilfeller, og deretter snakker vi om de praktiske implikasjonene til ARC.
For å eksperimentere med en hvilken som helst kode i denne delen, må du slå av automatisk referansetelling. Du kan gjøre dette ved å klikke på HelloObjectiveC prosjekt i Xcodes navigasjonspanel:
HelloObjectiveC-prosjektet i navigasjonspaneletDette åpner et vindu for å la deg justere bygginnstillingene for prosjektet. Vi diskuterer bygginnstillinger i andre halvdel av denne serien. For nå er alt vi trenger å finne, ARC-flagget. I søkefeltet øverst til høyre skriver du inn automatisk referanse telling, og du bør se følgende innstilling vises:
Deaktiverer automatisk referansetellingKlikk på pilene ved siden av Ja
og endre den til Nei
å deaktivere ARC for dette prosjektet. Dette lar deg bruke minnestyringsmetodene som er omtalt i de følgende avsnittene.
Manuell minnehåndtering (også kalt manuell lagring eller MMR) dreier seg om begrepet objekt "eierskap". Når du lager et objekt, blir du sagt til egen objektet - det er ditt ansvar å frigjøre objektet når du er ferdig med det. Dette er fornuftig, siden du ikke vil ha noe annet objekt å komme sammen med og slippe objektet mens du bruker det.
Objekt eierskap er implementert gjennom referanse telling. Når du krever eierskap til et objekt, øker du referansetellingen av en, og når du avstår eierskap, senker du referansetellingen med en. På denne måten er det mulig å sikre at et objekt aldri blir frigjort fra minnet mens et annet objekt bruker det. NSObject og NSObject-protokollen definerer de fire kjerne metoder som støtter objekt eierskap:
+(Id) Alloc
- Allokere minne for en ny forekomst og kreve eierskap av den forekomsten. Dette øker objektets referanse telling med en. Den returnerer en peker til det tildelte objektet.-(Id) beholde
- Gjør krav på eierskap av et eksisterende objekt. Det er mulig for et objekt å ha mer enn en eier. Dette øker også objektets referansetall. Den returnerer en peker til det eksisterende objektet.-(Void) slipper
- Oppgi eierskap av et objekt. Dette reduserer objektets referanse telling.-(Id) autoutgivelses
- Oppgi eierskap av et objekt på slutten av den nåværende autorelease poolblokken. Dette reduserer objektets referansetall, men lar deg fortsette å bruke objektet ved å utsette den faktiske utgivelsen til et senere tidspunkt. Den returnerer en peker til det eksisterende objektet.For hver Alloc
eller beholde
Metode du ringer, må du ringe utgivelse
eller autoutgivelses
på et tidspunkt nedover linjen. Antall ganger du krever et objekt må lik antall ganger du slipper den. Ringer en ekstra Alloc
/beholde
vil resultere i en minnelekkasje, og kaller en ekstra utgivelse
/autoutgivelses
vil prøve å få tilgang til et objekt som ikke eksisterer, noe som fører til at programmet ditt krasjer.
Alle objektinteraksjonene dine - uavhengig av om du bruker dem i en instansmetode, getter / setter eller en frittstående funksjon - bør følge kravet / bruk / fritt mønster, som vist i følgende eksempel:
Inkludert kodeeksempel: Manuell minne
int main (int argc, const char * argv []) // Krev objektet. Person * Frank = [[Personallokering] init]; // Bruk objektet. frank.name = @ "Frank"; NSLog (@ "% @", frank.name); // Frigjør objektet. [Frank release]; returner 0;
De [Personallokering]
anropssett oppriktig
referanse teller til en, og [frank utgivelse]
reduserer det til null, slik at kjøretiden kan avhende den. Legg merke til at du prøver å ringe en annen [frank utgivelse]
ville føre til et krasj, siden oppriktig
variabel eksisterer ikke lenger i minnet.
Når du bruker objekter som en lokal variabel i en funksjon (for eksempel det forrige eksempelet), er minnestyring ganske grei: bare ring utgivelse
på slutten av funksjonen. Imidlertid kan ting bli vanskeligere når man tilordner egenskaper inne i setter-metoder. For eksempel, vurder følgende grensesnitt for en ny klasse som heter Skip
:
Inkludert kodeeksempel: Manuell minne - svak referanse
// Ship.h #import "Person.h" @interface Ship: NSObject - (Person *) kaptein; - (void) setCaptain: (Person *) theCaptain; @slutt
Dette er en veldig enkel klasse med manuelt definerte tilgangsmetoder for a kaptein
eiendom. Fra et minnehåndteringsperspektiv er det flere måter som setter kan implementeres på. Først tar du det enkleste tilfellet der den nye verdien ganske enkelt er tilordnet en instansvariabel:
// Ship.m #import "Ship.h" @implementation Ship Person * _captain; - (Person *) kaptein return _captain; - (void) setCaptain: (Person *) theCaptain _captain = theCaptain; @slutt
Dette skaper en svak referanse fordi det Skip
eksempel tar ikke eierskap av kapteinen
objekt når det blir tildelt. Mens det ikke er noe galt med dette, og koden din vil fortsatt fungere, er det viktig å forstå konsekvensene av svake referanser. Vurder følgende utdrag:
#importere#import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool Person * frank = [[Personallokering] init]; Ship * discoveryOne = [[Ship alloc] init]; frank.name = @ "Frank"; [discoveryOne sattCaptain: frank]; NSLog (@ "% @", [discoveryOne kaptein] .navn); [Frank release]; // [discoveryOne kaptein] er nå ugyldig. NSLog (@ "% @", [discoveryOne kaptein]. Navn); [discoveryOne release]; returnere 0;
ringe [frank utgivelse]
svekkelser oppriktig
referanse teller til null, noe som betyr at runtime er tillatt å allokere det. Dette betyr at [discoveryOne kaptein]
peker nå på en ugyldig minneadresse, selv om discoveryOne
har aldri gitt ut det.
I prøvekoden som er oppgitt, vil du observere at vi har lagt til en dealloc
Overstyring av metode i Person-klassen. dealloc
kalles når minnet kommer til å bli utgitt. Vi bør vanligvis håndtere dealloc
og slipp alle nestede objektreferanser som vi holder. I dette tilfellet vil vi frigjøre den nestede navnegenskapen vi holder. Vi vil ha mer å si om dealloc
i neste kapittel.
Hvis du skulle prøve å få tilgang til eiendommen, ville programmet ditt mest sannsynlig krasje. Som du kan se, må du være veldig forsiktig med sporingsobjektreferanser når du bruker svake refererte egenskaper.
Svak referanse til kapteinverdienFor mer robuste objektrelasjoner kan du bruke sterke referanser. Disse blir opprettet ved å hevde objektet med a beholde
ring når den er tildelt:
Inkludert kodeeksempel: Manuell minne - sterk referanse
- (void) setCaptain: (Person *) theCaptain [_captain autorelease]; _captain = [theCaptain behold];
Med en sterk referanse, spiller det ingen rolle hva andre objekter gjør med kapteinen
objekt, siden beholde
sørger for at det forblir så lenge som Skip
eksempel trenger det. Selvfølgelig må du balansere beholde
ring ved å slippe den gamle verdien - hvis du ikke gjorde det, ville programmet lekke minne når noen tildelte en ny verdi til kaptein
eiendom.
De autoutgivelses
Metoden fungerer mye som utgivelse
, bortsett fra at objektets referansetall ikke forkortes umiddelbart. I stedet venter kjøretiden til slutten av gjeldende @autoreleasepool
blokk for å ringe en normal utgivelse
på objektet. Dette er grunnen til at main.m
mal er alltid innpakket i en @autoreleasepool
-Det sørger for at alle objekter står i kø med autoutgivelses
samtaler er faktisk utgitt på slutten av programmet:
int main (int argc, const char * argv []) @ autoreleasepool // Sett inn kode for å opprette og autorelease objekter her. NSLog (@ "Hei, Verden!"); // Eventuelle autoreleased objekter er * faktisk * utgitt her. returnere 0;
Ideen bak auto-releasing er å gi en objektets eier muligheten til å avstå eierskap uten å faktisk ødelegge objektet. Dette er et nødvendig verktøy i situasjoner der du må returnere et nytt objekt fra en fabrikkmetode. For eksempel, vurder følgende klassemetode som er definert i Ship.m
:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [skibet settkapten: den skaptiske]; return theShip;
Denne metoden oppretter, konfigurerer og returnerer en ny Skip
forekomst. Men det er et alvorlig problem med denne implementeringen: det resulterer i et minnelekkasje. Metoden avstår aldri eierskap av objektet, og innringere av shipWithCaptain
vet ikke at de trenger å frigjøre den returnerte gjenstanden (heller ikke de må). Som et resultat, skipet
Objektet vil aldri bli løslatt fra minnet. Dette er nettopp situasjonen autoutgivelses
ble designet for. Den riktige implementeringen er vist her:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [skibet settkapten: den skaptiske]; returnere [theShip autorelease]; // Må avstå eierskap!
Ved hjelp av autoutgivelses
i stedet for en umiddelbar utgivelse
Lar anroperen bruke det returnerte objektet samtidig som han avstår fra eierskapet til det på riktig sted. Hvis du husker fra kapitlet Datatyper, opprettet vi alle våre fundament datastrukturer ved hjelp av fabrikkmetoder på klassenivå. For eksempel:
NSSet * mannskap = [NSSet setWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", null];
De setWithObjects
Metoden fungerer akkurat som shipWithCaptain
metode beskrevet i det foregående eksempel. Den returnerer et autorelatert objekt slik at den som ringer kan bruke objektet uten å bekymre seg om minnehåndtering. Legg merke til at det finnes ekvivalente forekomstmetoder for å initialisere Foundation-objekter. For eksempel, mannskap
objekt i den siste prøven kan opprettes manuelt som følger:
// Opprett og gjenkjenn settet. NSSet * mannskap = [[NSSet alloc] initWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", null]; // Bruk settet ... // Frigjør settet. [mannskapsløsning];
Imidlertid bruker klassemetoder som setWithObjects
, arrayWithCapacity
, etc., er generelt foretrukket over Alloc
/i det
.
Å håndtere minnet bak et objekts egenskaper kan være en kjedelig, repeterende oppgave. For å forenkle prosessen inneholder Objective-C flere egenskapsattributter for å automatisere minnehåndteringsanropene i tilgangsfunksjoner. Attributtene som er beskrevet i den følgende listen, definerer setteradferdigheten i Håndbok referanse-telling miljøer. Gjøre ikke prøv å bruke tildele
og beholde
i et automatisk referanse telling miljø.
tildele
- Lagre en direkte peker til den nye verdien uten noen beholde
/ utgivelse
samtaler. Dette er den automatiserte ekvivalenten av en svak referanse.beholde
- Lagre en direkte peker til den nye verdien, men ring utgivelse
på den gamle verdien og beholde
på den nye. Dette er den automatiserte ekvivalenten av en sterk referanse.kopiere
- Opprett en kopi av den nye verdien. Kopiering hevder eierskap av den nye forekomsten, slik at forrige verdi sendes a utgivelse
budskap. Dette er som en sterk referanse til en helt ny forekomst av objektet. Vanligvis brukes kopiering kun til uforanderlige typer som NSString
.Som et enkelt eksempel, undersøk følgende eiendomserklæring:
@property (behold) Person * kaptein;
De beholde
Attributt forteller den tilknyttede @synthesize
erklæring om å lage en setter som ser ut som:
- (void) setCaptain: (Person *) theCaptain [_captain release]; _captain = [theCaptain behold];
Som du kan forestille deg, bruker du minnestyringsattributter med @eiendom
er mye enklere enn manuelt å definere getters og setters for hver eiendom i hver tilpasset klasse du definerer.
Nå som du har et håndtak på referanse telling, objekt eierskap og autorelease blokker, kan du helt glemme alt av det. Som i Xcode 4.2 og iOS 4 støtter Objective-C automatisk referansetelling (ARC), som er et pre-compilation-trinn som legger til i de nødvendige minnehåndteringsanropene for deg.
Hvis du har slått av ARC i forrige del, bør du slå den på igjen. Husk at du kan gjøre dette ved å klikke på HelloObjectiveC prosjekt i navigasjonspanelet, velg Bygg innstillinger fanen, og søker etter automatisk referanse telling.
Aktiverer automatisk referansetelling i prosjektets bygginnstillingerAutomatisk referansetelling fungerer ved å undersøke koden din for å finne ut hvor lenge et objekt trenger å holde seg fast og sette inn beholde
, utgivelse
, og autoutgivelses
metoder for å sikre at den blir avsatt når den ikke lenger trengs, men ikke mens du bruker den. For ikke å forvirre ARC-algoritmen, gjør du det må ikke gjøre noen beholde
, utgivelse
, eller autoutgivelses
kaller deg selv. For eksempel, med ARC, kan du skrive følgende metode og heller ikke skipet
eller kapteinen
vil bli lekket, selv om vi ikke eksplisitt avstod eierskap av dem:
Inkludert kodeeksempel: ARC
+ (Ship *) skipet Ship * theShip = [[Ship alloc] init]; Person * theCaptain = [[Personallokering] init]; [skipskapet kaptein: thecaptain]; return theShip;
I et ARC-miljø bør du ikke lenger bruke tildele
og beholde
eiendomsattributter. I stedet bør du bruke svak
og sterk
egenskaper:
svak
- Angi et ikke-eierskap forhold til destinasjonsobjektet. Dette er mye som tildele
; Den har imidlertid den praktiske funksjonaliteten til å sette egenskapen til nil
hvis verdien er allokert. På denne måten vil programmet ikke krasje når det prøver å få tilgang til en ugyldig minneadresse.sterk
- Angi et eierforhold til destinasjonsobjektet. Dette er ARC-ekvivalenten av beholde
. Det sikrer at et objekt ikke vil bli utgitt så lenge det er tildelt eiendommen.Du kan se forskjellen mellom svak og sterk ved å bruke implementeringen av skip
klassemetode fra foregående avsnitt. For å skape en sterk referanse til skipets kaptein, grensesnittet for Skip
bør se ut som følgende:
// Ship.h #import "Person.h" @interface Skip: NSObject @property (strong) Person * kaptein; + (Ship *) skipet; @slutt
Og gjennomføringen Skip
skal se ut som:
// Ship.m #import "Ship.h" @implementation Ship @synthesize captain = _captain; + (Ship *) skip Ship * theShip = [[Ship alloc] init]; Person * theCaptain = [[Personallokering] init]; [skipskapet kaptein: thecaptain]; return theShip; @slutt
Deretter kan du endre main.m
å vise skipets kaptein:
int main (int argc, const char * argv []) @ autoreleasepool Ship * ship = [Ship ship]; NSLog (@ "% @", [skip kaptein]); returnere 0;
Dette vil produsere noe som
i konsollen, som forteller oss at kapteinen
objekt opprettet i skip
klassemetode eksisterer fortsatt.
Men prøv å endre (sterk)
egenskapsattributt til (svak)
og re-kompilere programmet. Nå bør du se (null)
i utgangspanelet. Den svake referansen sikrer ikke at kapteinen
variabel pinner rundt, så en gang den kommer til slutten av skip
klassemetoden, mener ARC-algoritmen at den kan disponere kapteinen
. Som et resultat, kaptein
eiendommen er satt til nil
.
Minnehåndtering kan være en smerte, men det er en viktig del av å bygge et program. For iOS-applikasjoner er riktig gjenstandsallokering / -avhending spesielt viktig på grunn av begrensede minnesressurser for mobile enheter. Vi snakker mer om dette i den andre delen av denne serien, iOS Succinctly.
Heldigvis gjør den nye ARC-ordningen minnehåndtering mye lettere på den gjennomsnittlige utvikleren. I de fleste tilfeller er det mulig å behandle et ARC-prosjekt, akkurat som søppelkolleksjonen i et C # -program - bare opprett objekter og la ARC avhende dem etter eget skjønn. Vær imidlertid oppmerksom på at dette bare er en praktisk likt-ARC-gjennomføringen er mye mer effektiv enn søppelsamling.
Denne leksjonen representerer et kapittel fra Objective-C Succinctly, en gratis eBok fra teamet på Syncfusion.