Apprendre Objective-C Jour 5

Bienvenue à la cinquième partie de cette série sur Objective-C. Aujourd'hui, nous allons examiner la gestion de la mémoire, un élément d'Objective-C (et de nombreux autres langages) qui a tendance à faire trébucher les nouveaux programmeurs. La plupart des langages de script (tels que PHP) s’occupent automatiquement de la gestion de la mémoire, mais Objective-C exige que nous fassions attention à notre utilisation de la mémoire et que nous créions et libérons manuellement de l’espace pour nos objets..

Il est recommandé de garder trace de la quantité de mémoire utilisée par votre application afin de ne pas rencontrer de fuites ni de saturer la mémoire du système. C'est encore plus important sur les systèmes mobiles tels que l'iPhone où la mémoire est beaucoup plus limitée que sur un ordinateur de bureau.

Deux approches

En Objective-C, il existe deux méthodes de gestion de la mémoire, la première si le comptage de références est en cours et la seconde est le garbage collection. Vous pouvez les considérer comme manuelles et automatiques, car le comptage des références est un code ajouté par le programmeur et le ramassage des ordures est le système qui gère automatiquement notre mémoire. Une remarque importante est que la récupération de place ne fonctionne pas sur l'iPhone. C'est pourquoi nous ne verrons pas comment cela fonctionne. Si vous souhaitez programmer sur Mac, consultez la documentation d'Apple pour voir comment fonctionne le ramassage des ordures..

Comptage de références

Alors, comment gérons-nous notre mémoire dans nos applications? Tout d'abord, quand utilisons-nous la mémoire dans notre code? Lorsque vous créez une instance d'une classe (un objet), la mémoire est allouée et notre objet peut désormais fonctionner correctement. Désormais, un petit objet ne semble pas très intéressant, mais lorsque la taille de vos applications augmente, le problème devient rapidement énorme..

Regardons un exemple, disons que nous avons une sorte d'application de dessin et que chaque forme que l'utilisateur dessine est un objet séparé. Si l'utilisateur a dessiné 100 formes, nous avons 100 objets en mémoire. Supposons maintenant que l'utilisateur recommence et efface l'écran, puis dessine 100 autres objets. Si nous ne gérons pas notre mémoire correctement, nous allons nous retrouver avec 200 objets ne faisant rien de plus que de monopoliser la mémoire.

Nous corrigeons cela en comptant les références. Lorsque nous créons un nouvel objet et utilisons alloc, nos objets ont un nombre de retenues égal à 1. Si nous appelons une rétention sur cet objet, le nombre de retenues est maintenant de 2, etc. Si nous relâchons l'objet, le nombre de retenues décroît de 1. Alors que le nombre de retenues est non nul, notre objet reste figé, mais lorsque le nombre de retenues atteint zéro, le système libère notre objet, libérant ainsi de la mémoire..

Syntaxe

Il existe différentes méthodes que vous pouvez appeler qui auront un impact sur la gestion de la mémoire. Tout d'abord, lorsque vous créez un objet en utilisant un nom de méthode contenant alloc, new ou copy, vous en prenez possession. Cela est également vrai si vous utilisez la méthode rétention sur un objet. Une fois que vous relâchez ou autorelease (plus sur cela plus tard) un objet, vous ne prenez plus la propriété de cet objet et ne vous souciez pas de ce qui lui arrive.

Donc, si nous allouons un objet comme tel;

 myCarClass * car = [myCarClass alloc]; 

Nous sommes maintenant responsables de la voiture objet et nous devons la relâcher manuellement plus tard (ou autorelease). Il est important de noter que si vous deviez essayer de libérer manuellement un objet qui a été défini pour autorelease, l'application se bloquerait..

Depuis que nous avons créé notre objet en utilisant alloc, notre objet voiture a maintenant un nombre de retenues égal à 1, ce qui signifie qu'il ne sera pas désalloué. Si devait également conserver notre objet comme tel;

 [voiture retenir]; 

Alors notre nombre de retenues est maintenant de 2. Donc, afin de supprimer l'objet, nous devons relâcher deux fois pour que le nombre de retenues soit 0. Comme le nombre de retenues est maintenant égal à zéro, l'objet sera désalloué..

Pool Autorelease et Autorelease

Lorsque vous avez créé un nouveau projet XCode, vous avez peut-être remarqué que du code apparaît par défaut, ce qui crée un pool d'auto-relâchement. Jusqu'à présent, vous l'avez ignoré. Nous allons maintenant voir ce qu'il fait et où l'utiliser..

Le code que vous connaissez probablement déjà devrait ressembler à ceci:

 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [drain de piscine]; 

Remarque: si vous vous référez à une documentation plus ancienne, la dernière ligne peut être considérée comme une version, plutôt que comme un drain, il s’agit d’un ajout plus récent au langage, mais fait essentiellement la même chose..

A présent, vous devriez être capable de dire dans une certaine mesure ce que fait le code ci-dessus; il crée une instance de NSAutoReleasePool appelée pool, lui alloue de la mémoire, puis l'initialise à l'aide de la méthode init.

Lorsque nous envoyons le message autorelease à un objet, cet objet est ensuite ajouté au pool de libération automatique le plus interne (le plus interne, car les pools peuvent être imbriqués les uns dans les autres - nous en parlerons plus tard). Lorsque le pool envoie le message de vidange, tous les objets envoyés par le message de relargage automatique sont libérés; essentiellement, autorelease reporte la libération à plus tard..

Ceci est utile car de nombreuses méthodes qui renvoient un objet, généralement un objet autoreleased, ne nous inquiétant pas du nombre de retenues de l'objet que nous venons de recevoir ni de le libérer, car ce sera le cas. fait plus tard.

Pool Autorelease imbriqué

J'ai déjà brièvement parlé de la possibilité d'imbriquer des piscines auto-libérées, mais à quoi cela sert-il? Bien qu'il existe plusieurs utilisations, l'une des utilisations les plus courantes consiste à imbriquer un pool de libération automatique dans une boucle utilisant des objets temporaires..

Par exemple, si vous avez une boucle qui crée deux objets temporaires pour faire tout ce que vous voulez, si vous définissez ces deux objets sur autorelease, vous pouvez les utiliser jusqu'à ce que le pool reçoive le message de drain, sans avoir à vous soucier de la libération manuelle. désallouer. Apple a un bon exemple du moment où vous utiliseriez ce type de pool d'autorelease imbriqué dans sa documentation;

 void main () NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray * args = [[arguments de NSProcessInfo processInfo]]; for (NSString * NomFichier dans les arguments) NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init]; NSError * error = nil; NSString * fileContents = [[[[NSString alloc]] initWithContentsOfFile: encodage NomFichier: erreur NSUTF8StringEncoding: & erreur] autorelease]; / * Traite la chaîne en créant et autorelérant plus d’objets. * / [loopPool drain];  / * Faites le nettoyage nécessaire. * / [drain de piscine]; exit (EXIT_SUCCESS);  

Source: mmAutoreleasePools

L'exemple ci-dessus en a un peu plus que nécessaire, mais la structure est là. Comme vous pouvez le constater, lorsque l'application est ouverte et que main est chargé, un pool de libération automatique appelé pool est créé. Cela signifie que tout message autoreleased avant que le pool soit envoyé, le message de drain sera attribué à ce pool autorelease, sauf s'il se trouve à l'intérieur d'un pool autorelease situé dans celui-ci (désolé si cela semble un peu déroutant)..

Dans la boucle, un autre pool de libération automatique est créé, appelé loopPool. Cette piscine est drainée à l'intérieur de la boucle, de sorte que tout ce qui est auto-libéré à l'intérieur de la boucle est libéré avant que la boucle ne se répète (ou se termine).

Le pool autorelease interne n'a aucun effet sur le pool autorelease externe. Vous pouvez créer autant de pools autorelease que vous le souhaitez. Si nous utilisions autorelease dans la boucle ci-dessus sans disposer d'un pool distinct, tous les objets que nous étions en train de créer ne seraient pas libérés avant la fin de la tâche principale. Donc, si la boucle était exécutée 100 fois, nous aurions 100 objets qui accumulent de la mémoire et qui n'ont pas encore été libérés, ce qui gonfle notre application..

rétentionCompte

Avant de terminer, examinons quelque chose qui pourrait aider à faire de la gestion de la mémoire un chapitre plus facile à avaler. Jusqu'à présent, lorsque nous avons créé des objets, nous nous sommes souvenus du nombre de références d'un objet, etc., mais nous n'avons jamais vu le nombre réel. Pour les besoins de l’éducation, il existe une méthode que nous pouvons utiliser pour voir combien de références un objet a appelé retenueCount. La façon dont nous imprimons un RetainCount pour un objet ressemble à ceci;

 NSLog (@ "retenueCompte pour voiture:% d", [voiture retenueCompte]); 

retentionCount renvoie un entier, nous utilisons donc% d pour l'afficher dans la console. Il existe de rares cas (dans lesquels nous n'allons pas entrer) dans lesquels RetenirCompte peut être erroné et, en tant que tel, ne devrait pas être utilisé à 100% de manière programmatique. Elle est mise en œuvre uniquement pour le débogage, de sorte qu'une application ne doit jamais être mise en ligne à l'aide de la méthode rétentionCount..

Emballer

La gestion de la mémoire est un sujet que beaucoup de nouveaux programmeurs trouvent difficile, en particulier les programmeurs issus de langages qui s’occupent de tout pour vous. Nous avons couvert les bases, ce qui devrait être suffisant pour vous permettre de trouver votre place et de commencer à intégrer la gestion de la mémoire dans vos applications..

Apple dispose d'une fantastique bibliothèque de documentation destinée aux développeurs, disponible sur son site Web. Je vous recommande vivement de vérifier si vous n'êtes pas certain de quoi que ce soit que nous avons abordé aujourd'hui. Nous avons essayé de garder le didacticiel bref et centré sur le laser aujourd'hui pour vous aider à comprendre la gestion de la mémoire sans ajouter de fluff..

Les questions sont les bienvenues, comme d'habitude.

Défi

Expérimentez simplement avec la console en créant un objet simple contenant quelques variables synthétisées, créez quelques instances de cette classe, puis vérifiez le nombre de conservations à l'aide de la méthode retentionCount. La meilleure façon de comprendre la gestion de la mémoire est de lancer XCode et de jouer avec les commandes alloc et conservation, etc. N'oubliez pas que les accidents et les erreurs ne sont pas un mur de briques, car ils vous aideront en fin de compte à éviter les erreurs à l'avenir..

La prochaine fois

Dans la prochaine partie, nous examinerons les catégories, une fonctionnalité intéressante disponible dans Objective-C qui permet aux développeurs de gagner beaucoup de temps et de simplifier le code..