Minne måste tilldelas för varje objekt som din ansökan använder och det måste fördelas när din ansökan görs med den för att säkerställa att din ansökan använder minnet så effektivt som möjligt. Det är viktigt att förstå Objective-C: s minnehanteringsmiljö för att säkerställa att ditt program inte läcker minne eller försöker referera till objekt som inte längre existerar.
Räkna referenser till ett objektTill skillnad från C #, gör Objective-C inte använd skräpsamling. Istället använder den en referensräknande miljö som spårar hur många platser som använder ett objekt. Så länge det finns åtminstone en referens till objektet, säkerställer objektiv-C-runtime att objektet kommer att ligga i minnet. Men om det inte längre finns några referenser till objektet får körtiden frigöra objektet och använda minnet för något annat. Om du försöker komma åt ett objekt efter det att det släppts kommer ditt program sannolikt att krascha.
Det finns två ömsesidigt exklusiva sätt att hantera objektreferenser i mål-C:
ARC är det bästa sättet att hantera minne i nya applikationer, men det är fortfarande viktigt att förstå vad som händer under huven. Den första delen av det här kapitlet visar hur man manuellt spårar objektreferenser, och sedan pratar vi om de praktiska konsekvenserna av ARC.
För att experimentera med någon av koden i det här avsnittet måste du stänga av automatisk referensräkning. Du kan göra detta genom att klicka på HelloObjectiveC projekt i Xcodes navigeringspanel:
HelloObjectiveC-projektet i navigeringspanelenDetta öppnar ett fönster för att låta dig justera byggnadsinställningarna för projektet. Vi diskuterar byggnadsinställningar under andra halvan av denna serie. För närvarande är allt vi behöver hitta ARC-flaggan. I sökfältet i övre högra hörnet skriver du automatisk referensräkning, och du bör se följande inställning visas:
Inaktiverar automatisk referensräkningKlicka på pilarna bredvid Ja
och ändra den till Nej
för att inaktivera ARC för detta projekt. Detta låter dig använda minneshanteringsmetoderna som diskuteras i följande punkter.
Manuell minneshantering (även kallad manuell frigivning eller MMR) kretsar kring begreppet objekt "ägande". När du skapar ett objekt, sägs du till egen objektet - det är ditt ansvar att frigöra objektet när du är färdig med det. Det här är vettigt eftersom du inte vill ha något annat objekt att komma med och släppa objektet medan du använder det.
Objekt ägande implementeras genom referensräkning. När du hävdar äganderätt till ett objekt ökar du dess referensräkning med en, och när du avstår från ägandet, sänker du dess referensräkning med en. På det här sättet är det möjligt att se till att ett objekt aldrig blir befriat från minnet medan ett annat objekt använder det. NSObject och NSObject-protokollet definierar de fyra kärnmetoderna som stöder objektägande:
+(Id) alloc
- Tilldela minne för en ny instans och hävda äganderätten till det exemplet. Detta ökar objektets referensräkning med en. Den returnerar en pekare till det tilldelade objektet.-(Id) bibehåller
- Hävda äganderätt till ett befintligt objekt. Det är möjligt att ett objekt har mer än en ägare. Detta ökar också objektets referensräkning. Den returnerar en pekare till det befintliga objektet.-(Void) frisättning
- Avstå äganderätt till ett objekt. Detta minskar objektets referensräkning.-(Id) funktionen för automatisk
- Avstå äganderätten till ett objekt i slutet av det aktuella autorelease poolblocket. Detta minskar objektets referensräkning men låter dig fortsätta använda objektet genom att skjuta upp den aktuella utgåvan till en senare tidpunkt. Den returnerar en pekare till det befintliga objektet.För varje alloc
eller behålla
Metod du ringer, du måste ringa släpp
eller funktionen för automatisk
vid någon punkt ner linjen. Antalet gånger du hävdar ett objekt måste lika många gånger du släpper ut det. Ringer en extra alloc
/behålla
kommer att leda till minnesläckage och ringa extra släpp
/funktionen för automatisk
kommer att försöka få åtkomst till ett objekt som inte existerar, vilket orsakar att ditt program kraschar.
Alla dina objektinteraktioner - oavsett om du använder dem i en instansmetod, getter / setter eller en fristående funktion - bör följa kravet / använd / fritt mönster, vilket visas i följande exempel:
Inkluderat kodprov: Manuell minne
int main (int argc, const char * argv []) // Påstå objektet. Person * frank = [[Personallokering] init]; // Använd objektet. frank.name = @ "Frank"; NSLog (@ "% @", frank.name); // Frigör objektet. [frank release]; returnera 0;
De [Personallokering]
samtalsuppsättningar frank
referensräkning till en, och [frank release]
sänker det till noll, vilket gör att körtiden kan kasseras. Observera att du försöker ringa en annan [frank release]
skulle resultera i en krasch, sedan frank
variabel finns inte längre i minnet.
När du använder objekt som en lokal variabel i en funktion (t ex föregående exempel) är minneshanteringen ganska enkel: ring bara släpp
i slutet av funktionen. Men saker kan bli svårare när man tilldelar egenskaper inom setter metoder. Tänk på följande gränssnitt för en ny klass som heter Fartyg
:
Inkluderat kodprov: Manuell minne - svag referens
// Ship.h #import "Person.h" @interface Ship: NSObject - (Person *) kapten; - (void) setCaptain: (Person *) theCaptain; @slutet
Detta är en mycket enkel klass med manuellt definierade accessor metoder för a kapten
fast egendom. Ur ett minnehanteringsperspektiv finns det flera sätt som setter kan implementeras. Först, ta det enklaste fallet där det nya värdet helt enkelt tilldelas en instansvariabel:
// Ship.m #import "Ship.h" @implementation Fartyg Person * _captain; - (Person *) kapten return _captain; - (void) setCaptain: (Person *) theCaptain _captain = theCaptain; @slutet
Detta skapar en svag referens eftersom det Fartyg
exemplet tar inte ägande av kaptenen
objekt när det tilldelas Medan det inte finns något fel med detta, och din kod fortfarande fungerar, är det viktigt att förstå konsekvenserna av svaga referenser. Tänk på följande kod:
#importera#import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool Person * frank = [[Personallokering] init]; Fartyg * discoveryOne = [[Ship alloc] init]; frank.name = @ "Frank"; [discoveryOne setCaptain: frank]; NSLog (@ "% @", [discoveryOne captain] .name); [frank release]; // [discoveryOne captain] är nu ogiltig. NSLog (@ "% @", [discoveryOne kapten]. Namn); [discoveryOne release]; returnera 0;
Kallelse [frank release]
minskningar frank
referensräkning till noll, vilket betyder att runtime tillåts att fördela den. Detta innebär att [discoveryOne kapten]
pekar nu på en ogiltig minnesadress, även om discoveryOne
släppte aldrig det.
I den angivna provkoden kommer du att observera att vi har lagt till en dealloc
metodöverstyrning i personklassen. dealloc
kallas när minnet håller på att släppas. Vi bör vanligtvis hantera dealloc
och släpp alla nestade objektreferenser som vi håller. I detta fall kommer vi att släppa den nestade namnegenskapen som vi håller. Vi får mer att säga om dealloc
i nästa kapitel.
Om du skulle försöka komma åt fastigheten skulle ditt program sannolikt krascha. Som du kan se måste du vara mycket noggranna spårningsobjektreferenser när du använder svagt refererade egenskaper.
Svag referens till kaptenvärdetFör mer robusta objektrelationer kan du använda starka referenser. Dessa skapas genom att hävda objektet med a behålla
ring när den är tilldelad:
Inkluderat kodprov: Manuell minne - stark referens
- (void) setCaptain: (Person *) theCaptain [_captain autorelease]; _captain = [Kapten behåller];
Med en stark referens spelar det ingen roll vad andra objekt gör med kaptenen
objekt, eftersom behålla
ser till att det kommer att stanna kvar så länge som Fartyg
exemplet behöver det. Självklart måste du balansera behålla
ring genom att släppa det gamla värdet - om du inte gjorde det, skulle ditt program läcka minne när någon tilldelade ett nytt värde till kapten
fast egendom.
De funktionen för automatisk
Metoden fungerar mycket som släpp
, förutom att objektets referensräkning inte minskas omedelbart. Istället väntar runtime till slutet av strömmen @autoreleasepool
blockera för att ringa en normal släpp
på objektet. Det är därför main.m
mall är alltid insvept i en @autoreleasepool
-Det säkerställer att alla föremål är i kö med funktionen för automatisk
samtal är faktiskt släppt i slutet av programmet:
int main (int argc, const char * argv []) @autoreleasepool // Infoga kod för att skapa och autoreleera objekt här. NSLog (@ "Hej, Värld!"); // Alla auktoriserade objekt är * faktiskt * släppt här. returnera 0;
Tanken bakom auto-releasing är att ge objektets ägare möjlighet att avstå från ägande utan att förstöra objektet. Detta är ett nödvändigt verktyg i situationer där du måste returnera ett nytt objekt från en fabriksmetod. Tänk på följande klassmetod som definieras i Ship.m
:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [Skytten sättaKaptain: TheCaptian]; returnera shipen;
Den här metoden skapar, konfigurerar och returnerar en ny Fartyg
exempel. Men det finns ett allvarligt problem med denna implementering: det resulterar i en minnesläcka. Metoden avstår aldrig ägande av objektet och uppringare av shipWithCaptain
vet inte att de behöver frigöra det återlämnade objektet (ej heller borde de behöva). Som ett resultat av detta fartyget
objekt kommer aldrig att släppas från minnet. Detta är just situationen funktionen för automatisk
designades för. Det korrekta genomförandet visas här:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [Skytten sättaKaptain: TheCaptian]; returnera [theShip autorelease]; // Måste avstå äganderätt!
Använder sig av funktionen för automatisk
istället för omedelbar släpp
låter uppringaren använda det återvändande objektet samtidigt som han lämnar äganderätten till det på rätt plats. Om du kommer ihåg från kapitlet Datatyper skapade vi alla våra grunddata-strukturer genom att använda fabriksmetoder på klassnivå. Till exempel:
NSSet * besättning = [NSSet setWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", noll];
De setWithObjects
Metoden fungerar precis som shipWithCaptain
metod som beskrivits i föregående exempel. Den returnerar ett auktoriserat objekt så att den som ringer kan använda objektet utan att oroa sig för minneshantering. Observera att det finns motsvarande instansmetoder för att initiera Foundation-objekt. Till exempel, besättning
objekt i det sista provet kan skapas manuellt enligt följande:
// Skapa och hävda uppsättningen. NSSet * besättning = [[NSSet alloc] initWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", noll]; // Använd uppsättningen ... // Släpp satsen. [besättningsläge];
Men använder klassmetoder som setWithObjects
, arrayWithCapacity
, etc., är generellt föredragen över alloc
/i det
.
Att hantera minnet bakom objektets egenskaper kan vara en tråkig och upprepad uppgift. För att förenkla processen innehåller Objective-C flera egenskapsattribut för automatisering av minneshanteringssamtal i accessorfunktioner. De attribut som beskrivs i följande lista definierar setterbeteendet i manuell referensräknande miljöer. Do inte försök använda tilldela
och behålla
i en automatisk referensräkningsmiljö.
tilldela
- Spara en direkt pekare till det nya värdet utan något behålla
/ släpp
samtal. Detta är den automatiska motsvarigheten till en svag referens.behålla
- Spara en direkt pekare till det nya värdet, men ring släpp
på det gamla värdet och behålla
på den nya. Detta är den automatiska motsvarigheten till en stark referens.kopia
- Skapa en kopia av det nya värdet. Kopiering fordringar ägande av den nya instansen, så det föregående värdet skickas a släpp
meddelande. Det här är som en stark referens till en helt ny förekomst av objektet. I allmänhet används kopiering endast för oföränderliga typer som NSString
.Som ett enkelt exempel, undersök följande fastighetsdeklaration:
@property (behåll) Person * kapten;
De behålla
attributet berättar de associerade @synthesize
deklaration för att skapa en setter som ser ut som:
- (void) setCaptain: (Person *) theCaptain [_captain release]; _captain = [Kapten behåller];
Som du kan föreställa dig, använder du minneshanteringsattribut med @fast egendom
är mycket enklare än att manuellt definiera getters och setters för varje egendom i varje anpassad klass du definierar.
Nu när du har ett handtag på referensräkning, objektägande och autorelease-block kan du helt glömma allt. När det gäller Xcode 4.2 och iOS 4, stöder Objective-C automatisk referensräkning (ARC), vilket är ett församlingssteg som lägger till i nödvändiga minneshanteringssamtal för dig.
Om du råkade ha stängt av ARC i föregående avsnitt ska du sätta på den igen. Kom ihåg att du kan göra det genom att klicka på HelloObjectiveC projekt i navigeringspanelen, välj Bygg inställningar fliken och letar efter automatisk referensräkning.
Aktiverar automatisk referensräkning i projektets bygginställningarAutomatisk referensräkning fungerar genom att undersöka din kod för att räkna ut hur länge ett objekt behöver hålla sig fast och infoga behålla
, släpp
, och funktionen för automatisk
metoder för att säkerställa att det avlöps när det inte längre behövs, men inte när du använder det. För att inte förväxla ARC-algoritmen, du får inte göra några behålla
, släpp
, eller funktionen för automatisk
kallar dig själv. Till exempel med ARC kan du skriva följande metod och inte heller fartyget
inte heller kaptenen
kommer att läckas, även om vi inte uttryckligen avstår från ägande av dem:
Inkluderat kodprov: ARC
+ (Ship *) fartyg Ship * theShip = [[Ship alloc] init]; Person * theCaptain = [[Personallokering] init]; [Skytten satt Kapten: Kapten]; returnera shipen;
I en ARC-miljö borde du inte längre använda tilldela
och behålla
egendom attribut. I stället borde du använda svag
och stark
attribut:
svag
- Ange ett icke-ägande förhållande till destinationsobjektet. Det här är ungefär lika tilldela
; Det har dock den praktiska funktionaliteten att fastställa egenskapen till noll
om värdet är fördelat. På detta sätt kommer ditt program inte att krascha när det försöker komma åt en ogiltig minnesadress.stark
- Ange ett ägandeförhållande till destinationsobjektet. Detta är ARC-ekvivalent av behålla
. Det säkerställer att ett objekt inte kommer att släppas så länge det tilldelas fastigheten.Du kan se skillnaden mellan svag och stark med hjälp av implementeringen av fartyg
klassmetod från föregående avsnitt. För att skapa en stark referens till fartygets kapten, gränssnittet för Fartyg
ska se ut som följande:
// Ship.h #import "Person.h" @interface Ship: NSObject @property (stark) Person * kapten; + (Ship *) fartyg; @slutet
Och genomförandet Fartyg
ska se ut som:
// Ship.m #import "Ship.h" @implementation Ship @synthesize captain = _captain; + (Ship *) fartyg Ship * theShip = [[Ship alloc] init]; Person * theCaptain = [[Personallokering] init]; [Skytten satt Kapten: Kapten]; returnera shipen; @slutet
Då kan du ändra main.m
för att visa fartygets kapten:
int main (int argc, const char * argv []) @autoreleasepool Ship * ship = [Ship ship]; NSLog (@ "% @", [fartygs kapten]); returnera 0;
Detta kommer att mata ut något liknande
i konsolen, som berättar för oss att kaptenen
objekt skapat i fartyg
klassmetoden finns fortfarande.
Men försök att ändra (stark)
egenskap attribut till (svag)
och omregistrera programmet. Nu ska du se (null)
i utmatningspanelen. Den svaga referensen garanterar inte att kaptenen
variabla pinnar runt, så en gång det kommer i slutet av fartyg
klassmetoden anser ARC-algoritmen att den kan hantera kaptenen
. Som ett resultat av detta kapten
fastigheten är inställd på noll
.
Minneshantering kan vara en smärta, men det är en viktig del av att bygga en applikation. För iOS-applikationer är det lämpligt att tilldela / avyttra objektet på grund av de begränsade minnesresurserna hos mobila enheter. Vi talar mer om detta i den andra delen av denna serie, IOS Succinctly.
Lyckligtvis gör det nya ARC-systemet minnehanteringen mycket lättare på den genomsnittliga utvecklaren. I de flesta fall är det möjligt att behandla ett ARC-projekt precis som skräpuppsamlingen i ett C # -program. Skapa bara dina objekt och låt ARC sköta dem efter eget gottfinnande. Observera dock att det här bara är en praktisk likhet - ARC-genomförandet är mycket effektivare än skräpsamling.
Denna lektion representerar ett kapitel från Objective-C Succinctly, en gratis eBook från laget vid Syncfusion.