Geheugen moet worden toegewezen voor elk object dat door uw toepassing wordt gebruikt en moet worden verwijderd wanneer uw toepassing ermee klaar is om te zorgen dat uw toepassing het geheugen zo efficiënt mogelijk gebruikt. Het is belangrijk om de geheugenbeheeromgeving van Objective-C te begrijpen om ervoor te zorgen dat uw programma geen geheugen lekt of probeert te verwijzen naar objecten die niet meer bestaan.
Verwijzingen naar een object tellenIn tegenstelling tot C # doet Objective-C dat wel niet gebruik garbage collection. In plaats daarvan gebruikt het een referentie-tellende omgeving die bijhoudt hoeveel plaatsen een object gebruiken. Zolang er ten minste één verwijzing naar het object is, zorgt de runtime Objective-C ervoor dat het object zich in het geheugen bevindt. Als er echter geen verwijzingen naar het object meer zijn, kan de runtime het object vrijgeven en het geheugen voor iets anders gebruiken. Als u probeert toegang te krijgen tot een object nadat het is vrijgegeven, loopt uw programma waarschijnlijk vast.
Er zijn twee elkaar uitsluitende manieren om objectreferenties in Objective-C te beheren:
ARC is de geprefereerde manier om het geheugen te beheren in nieuwe applicaties, maar het is nog steeds belangrijk om te begrijpen wat er gebeurt onder de motorkap. In het eerste deel van dit hoofdstuk wordt beschreven hoe u objectreferenties handmatig bijhoudt en vervolgens bespreken we de praktische implicaties van ARC.
Als u wilt experimenteren met een code in deze sectie, moet u automatische referentietelling uitschakelen. U kunt dit doen door op te klikken HelloObjectiveC project in het navigatiepaneel van Xcode:
Het HelloObjectiveC-project in het navigatiepaneelHiermee wordt een venster geopend waarin u de buildinstellingen voor het project kunt aanpassen. We bespreken build-instellingen in de tweede helft van deze serie. Voor nu is alles wat we moeten vinden de ARC-vlag. Typ in het zoekveld in de rechterbovenhoek automatische referentietelling, en je zou de volgende instelling moeten zien verschijnen:
Automatische referentietelling uitschakelenKlik op de pijlen naast Ja
en verander het in Nee
om ARC voor dit project uit te schakelen. Hiermee kunt u de methoden voor geheugenbeheer gebruiken die in de volgende paragrafen worden besproken.
Handmatig geheugenbeheer (ook wel handmatige bewaarrelease of MMR genoemd) draait om het concept van het object "eigendom". Wanneer u een object maakt, wordt u gezegd eigen het object - het is jouw verantwoordelijkheid om het object te bevrijden als je er klaar mee bent. Dit is logisch, omdat je niet wilt dat een ander object langskomt en het object vrijgeeft terwijl je het gebruikt.
Objecteigendom wordt geïmplementeerd door middel van referentietelling. Wanneer u het eigendom van een object claimt, verhoogt u het referentietelling met één en wanneer u afstand doet van het eigendom, verlaagt u het referentietelling met één. Op deze manier is het mogelijk om ervoor te zorgen dat een object nooit uit het geheugen wordt bevrijd terwijl een ander object het gebruikt. NSObject en het NSObject-protocol definiëren de vier kernmethoden die objecteigendom ondersteunen:
+(Id) VERD
- Wijs geheugen toe voor een nieuw exemplaar en claim het eigendom van dat exemplaar. Hierdoor wordt het referentietelling van het object met één verhoogd. Het retourneert een aanwijzer naar het toegewezen object.-(Id) behouden
- Claim het eigendom van een bestaand object. Het is mogelijk dat een object meer dan één eigenaar heeft. Dit verhoogt ook de referentietelling van het object. Het retourneert een aanwijzer naar het bestaande object.-(Void) vrijgave
- Laat het eigendom van een object los. Hiermee wordt de referentietelling van het object verlaagd.-(Id) autorelease
- Geef het eigendom van een object op aan het einde van het huidige blok voor autoreleasepool. Hiermee wordt de referentietelling van het object verlaagd, maar kunt u het object blijven gebruiken door de daadwerkelijke release uit te stellen tot een later tijdstip. Het retourneert een aanwijzer naar het bestaande object.Voor iedere alloc
of behouden
methode die u belt, moet u bellen vrijlating
of autorelease
op een gegeven moment langs de lijn. Het aantal keren dat u een object claimt moet gelijk aan het aantal keren dat je het vrijgeeft. Een extra bellen alloc
/behouden
zal resulteren in een geheugenlek en een extra bellen vrijlating
/autorelease
zal proberen toegang te krijgen tot een object dat niet bestaat, waardoor je programma crasht.
Al uw objectinteracties, ongeacht of u ze gebruikt in een instantiemethode, getter / setter of een zelfstandige functie, moeten het claim / gebruik / gratis patroon volgen, zoals aangetoond in het volgende voorbeeld:
Inclusief codevoorbeeld: handmatig geheugen
int main (int argc, const char * argv []) // Claim het object. Persoon * frank = [[Person alloc] init]; // Gebruik het object. frank.name = @ "Frank"; NSLog (@ "% @", frank.name); // Maak het object vrij. [openhartige release]; retourneer 0;
De [Person alloc]
bel sets openhartig
de referentie telt voor één, en [vrijlating]
verlaagt het naar nul, waardoor de runtime het kan weggooien. Merk op dat u probeert een andere te bellen [vrijlating]
zou resulteren in een crash, aangezien de openhartig
variabele bestaat niet meer in het geheugen.
Wanneer u objecten gebruikt als een lokale variabele in een functie (bijvoorbeeld het vorige voorbeeld), is geheugenbeheer vrij eenvoudig: gewoon bellen vrijlating
aan het einde van de functie. Het kan echter lastiger worden om eigenschappen toe te wijzen binnen de settermethoden. Overweeg bijvoorbeeld de volgende interface voor een nieuwe klasse genaamd Schip
:
Inclusief codevoorbeeld: handmatig geheugen - zwakke referentie
// Ship.h #import "Person.h" @interface Schip: NSObject - (Persoon *) kapitein; - (ongeldig) setCaptain: (Persoon *) theCaptain; @einde
Dit is een heel eenvoudige klasse met handmatig gedefinieerde accessormethoden voor a gezagvoerder
eigendom. Vanuit het oogpunt van geheugenbeheer zijn er verschillende manieren waarop de setter kan worden geïmplementeerd. Neem eerst het eenvoudigste geval waarbij de nieuwe waarde eenvoudig wordt toegewezen aan een instantievariabele:
// Ship.m #import "Ship.h" @implementation Ship Person * _captain; - (Persoon *) kapitein return _captain; - (void) setCaptain: (Persoon *) theCaptain _captain = theCaptain; @end
Dit creëert een zwakke referentie omdat het Schip
instantie neemt geen eigendom van de de kapitein
object wanneer het wordt toegewezen. Hoewel er niets mis is en uw code nog steeds werkt, is het belangrijk om de implicaties van zwakke referenties te begrijpen. Beschouw het volgende fragment:
#importeren#import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool init []]; Ship * discoveryOne = [[Ship allocation] init]; frank.name = @ "Frank"; [discoveryOne setCaptain: frank]; NSLog (@ "% @", [discoveryOne captain] .name); [openhartige release]; // [discoveryOne captain] is nu ongeldig. NSLog (@ "% @", [discoveryOne captain]. Name); [discoveryOne release]; retourneer 0;
Roeping [vrijlating]
decrements openhartig
's referentietelling tot nul, wat betekent dat de runtime de kans krijgt om het te delokaliseren. Dit betekent dat [discoveryOne captain]
wijst nu naar een ongeldig geheugenadres, hoewel discoveryOne
nooit uitgebracht.
In de verstrekte voorbeeldcode, zult u merken dat we een hebben toegevoegd dealloc
methode-override in de persoonsklasse. dealloc
wordt gebeld wanneer het geheugen wordt vrijgegeven. We zouden het normaal moeten aanpakken dealloc
en laat alle geneste objectreferenties die we hebben vrij. In dit geval zullen we de geneste naameigenschap die we hebben vrijgeven. We zullen meer over te zeggen hebben dealloc
in het volgende hoofdstuk.
Als u probeert toegang te krijgen tot de accommodatie, loopt uw programma waarschijnlijk vast. Zoals u kunt zien, moet u zeer zorgvuldig objectreferenties bijhouden wanneer u eigenschappen waarnaar zwak wordt verwezen, gebruikt.
Zwakke verwijzing naar de kapiteinswaardeVoor sterkere objectrelaties kunt u sterke referenties gebruiken. Deze worden gemaakt door het object te claimen met een behouden
bellen wanneer het is toegewezen:
Inclusief codevoorbeeld: handmatig geheugen - sterke referentie
- (ongeldig) setCaptain: (Persoon *) theCaptain [_captain autorelease]; _captain = [theCaptain retain];
Met een sterke referentie maakt het niet uit wat andere objecten doen met de de kapitein
object, sinds behouden
zorgt ervoor dat het zo lang blijft als de Schip
instantie heeft het nodig. Natuurlijk moet je het evenwicht bewaren behouden
oproep door de oude waarde vrij te geven - als u dat niet deed, lekte uw programma geheugen als iemand een nieuwe waarde aan de gezagvoerder
eigendom.
De autorelease
methode werkt veel als vrijlating
, behalve het referentietelling van het object wordt niet onmiddellijk verlaagd. In plaats daarvan wacht de runtime tot het einde van de stroom @autoreleasepool
blokkeren om een normaal te bellen vrijlating
op het object. Dit is waarom het main.m
sjabloon is altijd ingepakt in een @autoreleasepool
-het zorgt ervoor dat alle objecten in de wachtrij staan autorelease
oproepen zijn werkelijk vrijgegeven aan het einde van het programma:
int main (int argc, const char * argv []) @autoreleasepool // Voeg code toe om objecten hier te creëren en te autorelease. NSLog (@ "Hallo, Wereld!"); // Alle autoreleased-objecten worden hier * daadwerkelijk * vrijgegeven. retourneer 0;
Het idee achter automatisch vrijgeven is om de eigenaar van een object de mogelijkheid te geven eigenaarschap op te geven zonder het object daadwerkelijk te vernietigen. Dit is een noodzakelijk hulpmiddel in situaties waarin u een nieuw object moet retourneren van een fabrieksmethode. Neem bijvoorbeeld de volgende klassemethode die is gedefinieerd in Ship.m
:
+ (Verzenden *) shipWithCaptain: (Persoon *) theCaptian Ship * theShip = [[Ship allocation] init]; [theShip setCaptain: theCaptian]; retourneer het schip;
Met deze methode wordt een nieuwe gemaakt, geconfigureerd en geretourneerd Schip
aanleg. Maar er is een serieus probleem met deze implementatie: het resulteert in een geheugenlek. De methode maakt nooit afstand van eigendom van het object en van bellers shipWithCaptain
weet niet dat ze het geretourneerde object moeten bevrijden (en dat moeten ze ook niet doen). Dientengevolge, de het schip
object zal nooit uit het geheugen worden vrijgegeven. Dit is precies de situatie autorelease
is ontworpen voor. De juiste implementatie wordt hier getoond:
+ (Verzenden *) shipWithCaptain: (Persoon *) theCaptian Ship * theShip = [[Ship allocation] init]; [theShip setCaptain: theCaptian]; terugkeer [theShip autorelease]; // Moet afstand doen van eigendom!
Gebruik makend van autorelease
in plaats van een onmiddellijke vrijlating
laat de beller het geretourneerde object gebruiken terwijl hij nog steeds eigenaar is van het object op de juiste locatie. Als u zich herinnert van het hoofdstuk Datatypen, hebben we al onze Foundation-gegevensstructuren gemaakt op basis van fabrieksnormen op fabrieksniveau. Bijvoorbeeld:
NSSet * crew = [NSSet setWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", geen];
De setWithObjects
methode werkt precies zoals de shipWithCaptain
methode beschreven in het vorige voorbeeld. Het geeft een object met autorelease terug, zodat de beller het object kan gebruiken zonder zich zorgen te hoeven maken over geheugenbeheer. Merk op dat er equivalente instantiemethoden zijn voor het initialiseren van Foundation-objecten. Bijvoorbeeld de bemanning
object in het laatste voorbeeld kan als volgt handmatig worden gemaakt:
// Creëer en claim de set. NSSet * crew = [[NSSet alloc] initWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", niets]; // Gebruik de set ... // Ontgrendel de set. [crew release];
Gebruik echter klassemethoden zoals setWithObjects
, arrayWithCapacity
, enz., heeft over het algemeen de voorkeur boven de alloc
/in het
.
Omgaan met het geheugen achter de eigenschappen van een object kan een saaie, repetitieve taak zijn. Om het proces te vereenvoudigen, bevat Objective-C verschillende eigenschapsattributen voor het automatiseren van geheugenbeheergesprekken in accessor-functies. De attributen die in de volgende lijst worden beschreven, definiëren het setter-gedrag in met de hand referentie-telomgevingen. Do niet probeer te gebruiken toewijzen
en behouden
in een automatische referentietellingomgeving.
toewijzen
- Bewaar een directe aanwijzer naar de nieuwe waarde zonder enige behouden
/ vrijlating
noemt. Dit is het geautomatiseerde equivalent van een zwakke referentie.behouden
- Bewaar een directe aanwijzer naar de nieuwe waarde, maar bel vrijlating
op de oude waarde en behouden
op de nieuwe. Dit is het geautomatiseerde equivalent van een sterke referentie.kopiëren
- Maak een kopie van de nieuwe waarde. Eigendom van de claim van het nieuwe exemplaar kopiëren, zodat de vorige waarde wordt verzonden a vrijlating
bericht. Dit is een sterke verwijzing naar een gloednieuw exemplaar van het object. Over het algemeen wordt kopiëren alleen gebruikt voor onveranderlijke typen zoals NSString
.Bekijk als eenvoudig voorbeeld de volgende eigenschapverklaring:
@property (behouden) Persoon * kapitein;
De behouden
attribuut vertelt de geassocieerde @synthetiseren
verklaring om een setter te maken die er ongeveer zo uitziet:
- (ongeldig) setCaptain: (Persoon *) theCaptain [_kaptain release]; _captain = [theCaptain retain];
Zoals je je kunt voorstellen, met behulp van geheugenbeheerattributen met @eigendom
is veel eenvoudiger dan het handmatig definiëren van getters en setters voor elke eigenschap van elke aangepaste klasse die u definieert.
Nu dat je een handvat hebt bij het tellen van referenties, het bezit van objecten en blokken autorelease, kun je het allemaal helemaal vergeten. Vanaf Xcode 4.2 en iOS 4 ondersteunt Objective-C automatische referentietelling (ARC), een pre-compilatiestap die zorgt voor de nodige geheugenbeheeroproepen voor u.
Als je in het vorige gedeelte ARC hebt uitgeschakeld, moet je dit weer inschakelen. Vergeet niet dat u dit kunt doen door op de. Te klikken HelloObjectiveC project in het navigatiepaneel, selecteer de Bouw instellingen tab en zoeken naar automatische referentietelling.
Automatische referentietelling inschakelen in de bouwinstellingen van het projectAutomatische referentietelling werkt door uw code te onderzoeken om erachter te komen hoe lang een object moet blijven plakken en invoegen behouden
, vrijlating
, en autorelease
methoden om ervoor te zorgen dat het wordt verwijderd wanneer het niet langer nodig is, maar niet wanneer u het gebruikt. Om het ARC-algoritme niet te verwarren, u moet niet maak er een behouden
, vrijlating
, of autorelease
roept jezelf op. Met ARC kunt u bijvoorbeeld de volgende methode schrijven en geen van beide het schip
noch de kapitein
zal worden gelekt, ook al hebben we niet expliciet afstand gedaan van het eigendom ervan:
Inclusief codevoorbeeld: ARC
+ (Verzenden *) verzenden Ship * theShip = [[Ship allocation] init]; Persoon * theCaptain = [[Person allocation] init]; [theShip setCaptain: theCaptain]; retourneer het schip;
In een ARC-omgeving zou u de. Niet langer moeten gebruiken toewijzen
en behouden
eigenschapkenmerken. Gebruik in plaats daarvan de zwak
en sterk
attributen:
zwak
- Geef een niet-eigenaarrelatie op met het doelobject. Dit lijkt veel op toewijzen
; het heeft echter de handige functionaliteit om de eigenschap in te stellen nul
als de waarde is verwijderd. Op deze manier zal je programma niet crashen wanneer het probeert toegang te krijgen tot een ongeldig geheugenadres.sterk
- Specificeer een eigendomsrelatie met het doelobject. Dit is het ARC-equivalent van behouden
. Het zorgt ervoor dat een object niet wordt vrijgegeven zolang het is toegewezen aan de property.Je ziet het verschil tussen zwak en sterk met de implementatie van de schip
klassemethode uit de vorige sectie. Om een sterke referentie naar de kapitein van het schip te creëren, de interface voor Schip
moet er als volgt uitzien:
// Ship.h #import "Person.h" @interface Schip: NSObject @property (strong) Persoon * kapitein; + (Schip *) schip; @einde
En de implementatie Schip
zou moeten lijken op:
// Ship.m #import "Ship.h" @implementation Ship @synthesize captain = _captain; + (Schip *) schip schip * theShip = [[schip allocatie] init]; Persoon * theCaptain = [[Person allocation] init]; [theShip setCaptain: theCaptain]; retourneer het schip; @end
Dan kun je veranderen main.m
om de kapitein van het schip te tonen:
int main (int argc, const char * argv []) @autoreleasepool Ship * ship = [Ship ship]; NSLog (@ "% @", [kapitein van het schip]); retourneer 0;
Dit zal iets als uitvoeren
in de console, die ons vertelt dat de de kapitein
object gemaakt in de schip
klassenmethode bestaat nog steeds.
Maar probeer het te veranderen (sterk)
eigenschapkenmerk aan (zwak)
en het opnieuw compileren van het programma. Nu zou je het moeten zien (nul)
in het uitvoerpaneel. De zwakke referentie garandeert niet dat het de kapitein
variabele stokjes rond, dus zodra het aan het einde van de schip
klassemethode, denkt het ARC-algoritme dat het kan beschikken over de kapitein
. Dientengevolge, de gezagvoerder
eigenschap is ingesteld op nul
.
Geheugenbeheer kan lastig zijn, maar het is een essentieel onderdeel van het bouwen van een applicatie. Voor iOS-toepassingen is de juiste objecttoewijzing / -afvoer bijzonder belangrijk vanwege de beperkte geheugenbronnen van mobiele apparaten. We zullen hier meer over praten in het tweede deel van deze serie, iOS bondig.
Gelukkig maakt het nieuwe ARC-schema geheugenbeheer veel gemakkelijker voor de gemiddelde ontwikkelaar. In de meeste gevallen is het mogelijk om een ARC-project net als de garbagecollection in een C # -programma te behandelen - maak gewoon uw objecten en laat ARC hier naar eigen goeddunken over beschikken. Merk echter op dat dit slechts een praktische overeenkomst is - de ARC-implementatie is veel efficiënter dan het verzamelen van garbage.
Deze les vertegenwoordigt een hoofdstuk uit Objective-C bondig, een gratis eBoek van het team van Syncfusion.