La mémoire doit être allouée à chaque objet utilisé par votre application et doit être désallouée une fois l’application terminée pour que votre application utilise la mémoire le plus efficacement possible. Il est important de comprendre l'environnement de gestion de la mémoire d'Objective-C pour vous assurer que votre programme ne perd pas de mémoire ou ne tente pas de référencer des objets qui n'existent plus..
Compter les références à un objetContrairement à C #, Objective-C ne ne pas utiliser la collecte des ordures. Au lieu de cela, il utilise un environnement de comptage de références qui permet de suivre le nombre d'emplacements utilisant un objet. Tant qu'il existe au moins une référence à l'objet, le moteur d'exécution Objective-C s'assure que l'objet réside en mémoire. Cependant, s'il n'y a plus aucune référence à l'objet, le moteur d'exécution est autorisé à libérer l'objet et à utiliser la mémoire pour autre chose. Si vous essayez d'accéder à un objet après sa publication, votre programme plantera probablement.
Il existe deux manières mutuellement exclusives de gérer les références d'objet dans Objective-C:
ARC est le moyen privilégié de gérer la mémoire dans les nouvelles applications, mais il est toujours important de comprendre ce qui se passe sous le capot. La première partie de ce chapitre vous explique comment suivre manuellement les références d'objet, puis nous aborderons les implications pratiques de l'ARC..
Pour expérimenter avec l'un des codes de cette section, vous devez désactiver le comptage automatique des références. Vous pouvez le faire en cliquant sur le bouton HelloObjectiveC projet dans le panneau de navigation de Xcode:
Le projet HelloObjectiveC dans le panneau de navigationCela ouvre une fenêtre pour vous permettre d'ajuster les paramètres de construction du projet. Nous discuterons des paramètres de construction dans la seconde moitié de cette série. Pour l'instant, tout ce que nous avons à trouver est le drapeau ARC. Dans le champ de recherche situé dans le coin supérieur droit, tapez comptage automatique des références, et vous devriez voir le réglage suivant apparaître:
Désactivation du comptage automatique des référencesCliquez sur les flèches à côté de Oui
et le changer en Non
désactiver ARC pour ce projet. Cela vous permettra d’utiliser les méthodes de gestion de la mémoire décrites dans les paragraphes suivants..
La gestion manuelle de la mémoire (également appelée rétention manuelle ou MMR) s’articule autour du concept de "propriété" des objets. Lorsque vous créez un objet, on vous dit posséder l'objet-il est de votre responsabilité de libérer l'objet lorsque vous avez terminé. Cela a du sens, car vous ne voudriez pas qu'un autre objet vienne et relâche l'objet pendant que vous l'utilisez.
La propriété d'objet est implémentée via le comptage de références. Lorsque vous revendiquez la propriété d'un objet, vous augmentez son nombre de références de un, et lorsque vous abandonnez la propriété, vous décrémentez son nombre de références de un. De cette manière, il est possible de s’assurer qu’un objet ne sera jamais libéré de la mémoire pendant qu’un autre objet l’utilise. NSObject et le protocole NSObject définissent les quatre méthodes principales qui prennent en charge la propriété des objets:
+(id) alloc
- Allouez de la mémoire pour une nouvelle instance et revendiquez la propriété de cette instance. Cela augmente le nombre de références de l'objet de un. Il retourne un pointeur sur l'objet alloué.-(id) retenir
- Revendiquer la propriété d'un objet existant. Il est possible qu'un objet ait plus d'un propriétaire. Cela incrémente également le nombre de références de l'objet. Il retourne un pointeur sur l'objet existant.-libération (vide)
- Renoncer à la propriété d'un objet. Cela réduit le nombre de références de l'objet.-(id) autorelease
- Abandonnez la propriété d'un objet à la fin du bloc de pool de libération automatique actuel. Ceci décrémente le nombre de références de l'objet, mais vous permet de continuer à utiliser l'objet en différant la version réelle jusqu'à une date ultérieure. Il retourne un pointeur sur l'objet existant.Pour chaque allouer
ou conserver
la méthode que vous appelez, vous devez appeler Libération
ou autorelease
à un moment donné sur la ligne. Le nombre de fois que vous réclamez un objet doit égal au nombre de fois que vous le relâchez. Appeler un extra allouer
/conserver
entraînera une fuite de mémoire, et appelant un extra Libération
/autorelease
va essayer d'accéder à un objet qui n'existe pas, ce qui provoque le blocage de votre programme.
Toutes vos interactions d'objet, que vous les utilisiez dans une méthode d'instance, une méthode getter / setter ou une fonction autonome, doivent suivre le modèle revendication / utilisation / libre, comme illustré dans l'exemple suivant:
Échantillon de code inclus: Mémoire manuelle
int main (int argc, const char * argv []) // Réclamer l'objet. Person * frank = [[Person alloc] init]; // Utiliser l'objet. frank.name = @ "Frank"; NSLog (@ "% @", frank.name); // Libère l'objet. [libération franche]; retourne 0;
le [Personne allouer]
postes d'appel franc
Le nombre de références de à un, et [libération franche]
le décrémente à zéro, permettant au runtime de s'en débarrasser. Notez que d'essayer d'appeler un autre [libération franche]
entraînerait un crash, puisque le franc
la variable n'existe plus en mémoire.
Lorsque vous utilisez des objets en tant que variable locale dans une fonction (par exemple, l'exemple précédent), la gestion de la mémoire est assez simple: il suffit d'appeler Libération
à la fin de la fonction. Cependant, les choses peuvent devenir plus compliquées lors de l'affectation de propriétés dans les méthodes de définition. Par exemple, considérons l’interface suivante pour une nouvelle classe appelée Navire
:
Échantillon de code inclus: Mémoire manuelle - référence faible
// Ship.h #import "Person.h" @interface Ship: NSObject - (Person *) captain; - (vide) setCaptain: (Person *) theCaptain; @fin
C'est une classe très simple avec des méthodes d'accesseur définies manuellement pour un capitaine
propriété. Du point de vue de la gestion de la mémoire, le configurateur peut être implémenté de plusieurs manières. Tout d'abord, prenons le cas le plus simple où la nouvelle valeur est simplement affectée à une variable d'instance:
// Ship.m #import "Ship.h" @implementation Ship Person * _captain; - (Person *) capitaine return _captain; - (void) setCaptain: (Person *) theCaptain _captain = theCaptain; @fin
Cela crée un référence faible parce que le Navire
l'instance ne prend pas possession de la le capitaine
objet quand il est assigné. Bien qu'il n'y ait rien de mal à cela et que votre code fonctionne toujours, il est important de comprendre les implications de références faibles. Considérez l'extrait suivant:
#importation#import "Person.h" #import "Ship.h" int main (int argc, const car * argv []) @autoreleasepool Person * frank = [[Person alloc] init]; Ship * discoveryOne = [[Ship alloc] init]; frank.name = @ "Frank"; [discoveryOne setCaptain: frank]; NSLog (@ "% @", [capitaine de découverteOne] .name); [libération franche]; // [découverteOne capitaine] est maintenant invalide. NSLog (@ "% @", [capitaine de discoveryOne]. Name); [découverteOne release]; retourne 0;
Appel [libération franche]
décements franc
Le nombre de références de 'à zéro, ce qui signifie que le moteur d'exécution est autorisé à le désallouer. Cela signifie que [découverteOne capitaine]
pointe maintenant vers une adresse mémoire invalide, même si découverteOne
jamais sorti.
Dans l'exemple de code fourni, vous remarquerez que nous avons ajouté un dealloc
remplacement de méthode dans la classe Person. dealloc
est appelée lorsque la mémoire est sur le point d'être libérée. Nous devrions généralement gérer dealloc
et libérez toutes les références d'objet imbriquées que nous détenons. Dans ce cas, nous publierons la propriété du nom imbriqué que nous détenons. Nous aurons plus à dire sur dealloc
dans le chapitre suivant.
Si vous tentiez d'accéder à la propriété, votre programme échouerait probablement. Comme vous pouvez le constater, vous devez faire très attention au suivi des références d’objet lorsque vous utilisez des propriétés faiblement référencées..
Faible référence à la valeur du capitainePour des relations d'objet plus robustes, vous pouvez utiliser des références fortes. Ceux-ci sont créés en réclamant l'objet avec un conserver
appeler quand il est assigné:
Échantillon de code inclus: Mémoire manuelle - référence forte
- (vide) setCaptain: (Person *) theCaptain [_captain autorelease]; _captain = [theCaptain conserve];
Avec une référence forte, peu importe ce que font les autres objets avec le le capitaine
objet, depuis conserver
fait en sorte qu'il reste aussi longtemps que le Navire
l'instance en a besoin. Bien sûr, vous devez équilibrer le conserver
appelez en libérant l’ancienne valeur. Sinon, votre programme perdrait de la mémoire chaque fois que quelqu'un attribuerait une nouvelle valeur à la fonction. capitaine
propriété.
le autorelease
méthode fonctionne beaucoup comme Libération
, sauf que le compte de référence de l'objet n'est pas décrémenté immédiatement. Au lieu de cela, le runtime attend jusqu'à la fin du courant @autoreleasepool
bloquer d'appeler une normale Libération
sur l'objet. C'est pourquoi le main.m
le modèle est toujours emballé dans un @autoreleasepool
-il s'assure que tous les objets en file d'attente avec autorelease
les appels sont réellement publié à la fin du programme:
int main (int argc, const char * argv []) @autoreleasepool // Insérez le code pour créer et autorelease des objets ici. NSLog (@ "Hello, World!"); // Tous les objets autoreleased sont * effectivement * publiés ici. retourne 0;
L'idée derrière la libération automatique est de donner au propriétaire d'un objet la possibilité de renoncer à sa propriété sans détruire l'objet. C'est un outil nécessaire dans les situations où vous devez renvoyer un nouvel objet à partir d'une méthode d'usine. Par exemple, considérons la méthode de classe suivante définie dans Ship.m
:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [theShip setCaptain: le Capitaine]; renvoyez theShip;
Cette méthode crée, configure et retourne une nouvelle Navire
exemple. Mais cette implémentation pose un grave problème: elle provoque une fuite de mémoire. La méthode ne renonce jamais à la propriété de l'objet, et les appelants de shipWithCaptain
ne savent pas qu'ils doivent libérer l'objet renvoyé (ils ne devraient pas non plus le faire). En conséquence, le Le bateau
l'objet ne sera jamais libéré de la mémoire. C'est précisément la situation autorelease
a été conçu pour. La bonne implémentation est montrée ici:
+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Ship alloc] init]; [theShip setCaptain: le Capitaine]; return [theShip autorelease]; // Doit abandonner la propriété!
En utilisant autorelease
au lieu d'un immédiat Libération
permet à l'appelant d'utiliser l'objet renvoyé tout en en laissant la propriété au bon endroit. Si vous vous souvenez du chapitre Types de données, nous avons créé toutes nos structures de données Foundation à l'aide de méthodes d'usine au niveau de la classe. Par exemple:
NSSet * crew = [NSSet setWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", nil];
le setWithObjects
méthode fonctionne exactement comme le shipWithCaptain
méthode décrite dans l'exemple précédent. Il renvoie un objet autoreleased afin que l'appelant puisse utiliser l'objet sans se soucier de la gestion de la mémoire. Notez qu'il existe des méthodes d'instance équivalentes pour l'initialisation des objets Foundation. Par exemple, le équipage
L’objet du dernier exemple peut être créé manuellement comme suit:
// Crée et revendique l'ensemble. NSSet * crew = [[NSSet alloc] initWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", rien]; // Utiliser le jeu… // Libérer le jeu. [libération de l'équipage];
Cependant, en utilisant des méthodes de classe comme setWithObjects
, arrayWithCapacity
, etc., est généralement préféré au allouer
/init
.
Traiter la mémoire derrière les propriétés d'un objet peut être une tâche fastidieuse et répétitive. Pour simplifier le processus, Objective-C inclut plusieurs attributs de propriété permettant d’automatiser les appels de gestion de la mémoire dans les fonctions d’accesseur. Les attributs décrits dans la liste suivante définissent le comportement du configurateur dans Manuel environnements de comptage de références. Faire ne pas Essayez d'utiliser attribuer
et conserver
dans un environnement de comptage automatique des références.
attribuer
- Stocker un pointeur direct sur la nouvelle valeur sans aucun conserver
/ Libération
appels. C'est l'équivalent automatisé d'une référence faible.conserver
- Stocke un pointeur direct sur la nouvelle valeur, mais appelle Libération
sur l'ancienne valeur et conserver
sur le nouveau. C'est l'équivalent automatisé d'une référence forte.copie
- Créez une copie de la nouvelle valeur. La copie revendiquant la propriété de la nouvelle instance, la valeur précédente est donc envoyée à Libération
message. Cela ressemble à une forte référence à une toute nouvelle instance de l'objet. Généralement, la copie n’est utilisée que pour les types immuables tels que NSString
.Comme exemple simple, examinez la déclaration de propriété suivante:
@property (keep) Person * capitaine;
le conserver
attribut dit le associé @ synthétiser
déclaration pour créer un passeur qui ressemble à quelque chose comme:
- (vide) setCaptain: (Person *) theCaptain [_captain release]; _captain = [theCaptain conserve];
Comme vous pouvez l’imaginer, l’utilisation des attributs de gestion de la mémoire avec @propriété
est beaucoup plus facile que de définir manuellement les getters et les setters pour chaque propriété de chaque classe personnalisée que vous définissez.
Maintenant que vous maîtrisez le comptage des références, la propriété des objets et les blocs autorelease, vous pouvez complètement les oublier. À partir de Xcode 4.2 et iOS 4, Objective-C prend en charge le comptage automatique des références (ARC), une étape de précompilation qui ajoute les appels de gestion de la mémoire nécessaires..
Si vous avez désactivé ARC dans la section précédente, vous devez le réactiver. Rappelez-vous que vous pouvez le faire en cliquant sur le bouton HelloObjectiveC projet dans le panneau de navigation, en sélectionnant le Paramètres de construction onglet, et la recherche de comptage automatique des références.
Activation du comptage automatique des références dans les paramètres de construction du projetLe comptage automatique des références consiste à examiner votre code pour déterminer la durée pendant laquelle un objet doit rester en place et à l'insérer. conserver
, Libération
, et autorelease
méthodes pour vous assurer qu'il est désalloué lorsqu'il n'est plus nécessaire, mais pas pendant que vous l'utilisez. Pour ne pas confondre l'algorithme ARC, vous ne doit pas Faire n'importe conserver
, Libération
, ou autorelease
vous appelle. Par exemple, avec ARC, vous pouvez écrire la méthode suivante et ni Le bateau
ni le capitaine
seront divulgués, même si nous ne les avons pas explicitement abandonnés:
Échantillon de code inclus: ARC
+ (Ship *) ship Ship * theShip = [[Ship alloc]] init]; Person * theCaptain = [[Person alloc] init]; [theShip setCaptain: le Capitaine]; renvoyez theShip;
Dans un environnement ARC, vous ne devriez plus utiliser le attribuer
et conserver
attributs de propriété. Au lieu de cela, vous devriez utiliser le faible
et fort
les attributs:
faible
- Spécifiez une relation non-propriétaire avec l'objet de destination. Cela ressemble beaucoup à attribuer
; cependant, il a la fonctionnalité pratique de définir la propriété sur néant
si la valeur est désallouée. De cette façon, votre programme ne plantera pas lorsqu'il tentera d'accéder à une adresse mémoire invalide..fort
- Spécifiez une relation de propriétaire avec l'objet de destination. Ceci est l'équivalent ARC de conserver
. Cela garantit qu'un objet ne sera pas libéré tant qu'il est affecté à la propriété.Vous pouvez voir la différence entre faible et fort en utilisant la mise en œuvre du navire
méthode de classe de la section précédente. Pour créer une référence forte au capitaine du navire, l'interface de Navire
devrait ressembler à ceci:
// Ship.h #import "Person.h" @interface Ship: NSObject @property (strong) Person * captain; + (Navire *) navire; @fin
Et la mise en place Navire
devrait ressembler à:
// Ship.m #import "Ship.h" @implementation Ship @synthesize captain = _captain; + (Ship *) ship Ship * theShip = [[Affectation du navire] init]; Person * theCaptain = [[Person alloc] init]; [theShip setCaptain: le Capitaine]; renvoyez theShip; @fin
Ensuite, vous pouvez changer main.m
pour afficher le capitaine du navire:
int main (int argc, const char * argv []) @autoreleasepool Ship * ship = [Ship ship]; NSLog (@ "% @", [capitaine de navire]); retourne 0;
Cela produira quelque chose comme
dans la console, qui nous dit que le le capitaine
objet créé dans le navire
la méthode de classe existe toujours.
Mais essayez de changer le (fort)
attribut de propriété à (faible)
et recompiler le programme. Maintenant, vous devriez voir (nul)
dans le panneau de sortie. La référence faible ne garantit pas que le le capitaine
bâton variable autour, donc une fois qu'il arrive à la fin de la navire
méthode de classe, l'algorithme ARC pense qu'il peut disposer de le capitaine
. En conséquence, le capitaine
la propriété est définie sur néant
.
La gestion de la mémoire peut être une douleur, mais c'est une partie essentielle de la construction d'une application. Pour les applications iOS, une allocation / élimination appropriée des objets est particulièrement importante en raison des ressources de mémoire limitées des appareils mobiles. Nous en reparlerons dans la deuxième partie de cette série., iOS succinctement.
Heureusement, le nouveau schéma ARC facilite beaucoup la gestion de la mémoire pour le développeur moyen. Dans la plupart des cas, il est possible de traiter un projet ARC de la même manière que la récupération de place dans un programme C #: il suffit de créer vos objets et de laisser ARC les supprimer à sa discrétion. Notez cependant qu’il s’agit simplement d’une similarité pratique: l’implémentation ARC est beaucoup plus efficace que le garbage collection..
Cette leçon représente un chapitre de Objective-C Succinctly, un eBook gratuit de l’équipe de Syncfusion..