Question

Je commence tout juste à m'intéresser à Objective-C et Cocoa en vue de jouer avec le SDK iPhone.Je suis raisonnablement à l'aise avec les C malloc et free concept, mais le système de comptage des références de Cocoa me rend plutôt confus.On me dit que c'est très élégant une fois qu'on le comprend, mais je n'ai pas encore surmonté la bosse.

Comment faire release, retain et autorelease travail et quelles sont les conventions concernant leur utilisation ?

(Ou à défaut, qu’avez-vous lu qui vous a aidé à l’obtenir ?)

Était-ce utile?

La solution

Commençons avec retain et release; autorelease n'est en réalité qu'un cas particulier une fois que vous avez compris les concepts de base.

Dans Cocoa, chaque objet garde une trace du nombre de fois où il est référencé (en particulier, le NSObject la classe de base implémente cela).En appelant retain sur un objet, vous lui dites que vous souhaitez augmenter son nombre de références de un.En appelant release, vous indiquez à l'objet que vous le lâchez et son nombre de références est décrémenté.Si, après avoir appelé release, le décompte de références est désormais nul, alors la mémoire de cet objet est libérée par le système.

La manière fondamentale dont cela diffère de malloc et free est qu'un objet donné n'a pas à s'inquiéter du crash d'autres parties du système, car vous avez libéré la mémoire qu'ils utilisaient.En supposant que tout le monde joue le jeu et conserve/libère conformément aux règles, lorsqu'un morceau de code conserve puis libère l'objet, tout autre morceau de code faisant également référence à l'objet ne sera pas affecté.

Ce qui peut parfois être déroutant, c'est de connaître les circonstances dans lesquelles vous devez appeler retain et release.Ma règle générale est que si je veux conserver un objet pendant un certain temps (s'il s'agit d'une variable membre d'une classe, par exemple), je dois m'assurer que le nombre de références de l'objet me connaît.Comme décrit ci-dessus, le décompte de références d'un objet est incrémenté en appelant retain.Par convention, il est également incrémenté (mis à 1, en fait) lorsque l'objet est créé avec une méthode "init".Dans l'un ou l'autre de ces cas, il est de ma responsabilité d'appeler release sur l'objet quand j'en ai fini avec.Si je ne le fais pas, il y aura une fuite de mémoire.

Exemple de création d'objet :

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Maintenant pour autorelease.La libération automatique est utilisée comme un moyen pratique (et parfois nécessaire) pour demander au système de libérer cet objet après un petit moment.Du point de vue de la plomberie, lorsque autorelease est appelé, le thread actuel NSAutoreleasePool est alerté de l’appel.Le NSAutoreleasePool sait maintenant qu'une fois qu'il en a l'opportunité (après l'itération en cours de la boucle d'événements), il peut appeler release sur l'objet.De notre point de vue de programmeurs, il se charge d'appeler release pour nous, donc nous n'avons pas à le faire (et en fait, nous ne devrions pas le faire).

Ce qu'il est important de noter est que (encore une fois, par convention) toute création d'objet classe les méthodes renvoient un objet libéré automatiquement.Par exemple, dans l'exemple suivant, la variable « s » a un nombre de références de 1, mais une fois la boucle d'événements terminée, elle sera détruite.

NSString* s = [NSString stringWithString:@"Hello World"];

Si vous voulez conserver cette chaîne, vous devrez appeler retain explicitement, puis explicitement release quand vous aurez fini.

Considérez le morceau de code suivant (très artificiel) et vous verrez une situation où autorelease est requis:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

Je me rends compte que tout cela est un peu déroutant – à un moment donné, cependant, ça va cliquer.Voici quelques références pour vous aider :

  • Présentation d'Apple à la gestion de la mémoire.
  • Programmation Cocoa pour Mac OS X (4e édition), d'Aaron Hillegas - un livre très bien écrit avec de nombreux exemples intéressants.Cela se lit comme un tutoriel.
  • Si vous êtes vraiment en train de plonger, vous pourriez vous diriger vers Grand ranch nerd.Il s'agit d'un centre de formation dirigé par Aaron Hillegas, l'auteur du livre mentionné ci-dessus.J'y ai suivi le cours d'introduction au cacao il y a plusieurs années et c'était une excellente façon d'apprendre.

Autres conseils

Si vous comprenez le processus de conservation/publication, il existe deux règles d'or qui sont "duh" évidentes pour les programmeurs Cocoa établis, mais qui sont malheureusement rarement énoncées clairement pour les nouveaux arrivants.

  1. Si une fonction qui renvoie un objet a alloc, create ou copy en son nom alors l'objet est le vôtre.Tu dois appeler [object release] quand vous en aurez fini.Ou CFRelease(object), s'il s'agit d'un objet Core-Foundation.

  2. S'il n'y a PAS un de ces mots dans son nom, alors l'objet appartient à quelqu'un d'autre.Tu dois appeler [object retain] si vous souhaitez conserver l'objet après la fin de votre fonction.

Vous seriez bien servi de suivre également cette convention dans les fonctions que vous créez vous-même.

(Les pinailleux :Oui, il existe malheureusement quelques appels API qui font exception à ces règles mais ils sont rares).

Si vous écrivez du code pour le bureau et que vous pouvez cibler Mac OS X 10.5, vous devriez au moins envisager d'utiliser le garbage collection Objective-C.Cela simplifiera vraiment la plupart de votre développement – ​​c’est pourquoi Apple a déployé tous les efforts pour le créer en premier lieu et le faire fonctionner correctement.

Quant aux règles de gestion de la mémoire lorsque vous n'utilisez pas GC :

  • Si vous créez un nouvel objet en utilisant +alloc/+allocWithZone:, +new, -copy ou -mutableCopy ou si vous -retain un objet, vous en devenez propriétaire et devez assurer son envoi -release.
  • Si vous recevez un objet d'une autre manière, vous êtes pas le propriétaire et devrait pas assurez-vous qu'il est envoyé -release.
  • Si vous voulez vous assurer qu'un objet est envoyé -release vous pouvez soit l'envoyer vous-même, soit envoyer l'objet -autorelease et le courant pool de libération automatique je l'enverrai -release (une fois par reçu -autorelease) lorsque la piscine est vidangée.

Typiquement -autorelease est utilisé pour garantir que les objets restent actifs pendant toute la durée de l'événement en cours, mais sont nettoyés par la suite, car il existe un pool de libération automatique qui entoure le traitement des événements de Cocoa.Dans le cacao, c'est loin Il est plus courant de renvoyer des objets à un appelant qui sont libérés automatiquement que de renvoyer des objets que l'appelant lui-même doit libérer.

Utilisations Objective-C Comptage de référence, ce qui signifie que chaque objet a un nombre de références.Lorsqu'un objet est créé, il a un nombre de références de "1".En termes simples, lorsqu'un objet est référencé (c'est-à-dire stocké quelque part), il est "conservé", ce qui signifie que son nombre de références est augmenté de un.Lorsqu'un objet n'est plus nécessaire, il est "libéré", ce qui signifie que son nombre de références est diminué de un.

Lorsque le nombre de références d'un objet est 0, l'objet est libéré.Il s’agit d’un comptage de références de base.

Pour certains langages, les références sont automatiquement augmentées et diminuées, mais objective-c ne fait pas partie de ces langages.Ainsi, le programmeur est responsable de la conservation et de la libération.

Une manière typique d’écrire une méthode est la suivante :

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

Le problème de devoir se rappeler de libérer toutes les ressources acquises dans le code est à la fois fastidieux et sujet aux erreurs.Objective-C introduit un autre concept visant à rendre cela beaucoup plus facile :Pools à libération automatique.Les pools de libération automatique sont des objets spéciaux installés sur chaque thread.Il s'agit d'une classe assez simple, si vous recherchez NSAutoreleasePool.

Lorsqu'un objet reçoit un message "autorelease", l'objet recherchera tous les pools de libération automatique présents sur la pile pour ce thread actuel.Il ajoutera l'objet à la liste en tant qu'objet auquel envoyer un message de « libération » à un moment donné dans le futur, qui est généralement lorsque le pool lui-même est libéré.

En prenant le code ci-dessus, vous pouvez le réécrire pour qu'il soit plus court et plus facile à lire en disant :

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Étant donné que l'objet est libéré automatiquement, nous n'avons plus besoin d'appeler explicitement "release" dessus.En effet, nous savons qu'un pool de libération automatique le fera pour nous plus tard.

J'espère que cela aide.L'article de Wikipédia est plutôt bon sur le comptage de références.Plus d'informations sur Les pools de libération automatique peuvent être trouvés ici.Notez également que si vous construisez pour Mac OS X 10.5 et versions ultérieures, vous pouvez demander à Xcode de construire avec le garbage collection activé, ce qui vous permet d'ignorer complètement conserver/libérer/libérer automatiquement.

Joshua (#6591) - Les fonctionnalités de collecte des déchets dans Mac OS X 10.5 semblent plutôt sympas, mais ne sont pas disponibles pour l'iPhone (ou si vous souhaitez que votre application fonctionne sur les versions antérieures à 10.5 de Mac OS X).

De plus, si vous écrivez une bibliothèque ou quelque chose qui pourrait être réutilisé, l'utilisation du mode GC oblige toute personne utilisant le code à utiliser également le mode GC. Ainsi, si je comprends bien, toute personne essayant d'écrire du code largement réutilisable a tendance à opter pour la gestion. mémoire manuellement.

Comme toujours, lorsque les gens commencent à essayer de reformuler le document de référence, ils se trompent presque invariablement ou fournissent une description incomplète.

Apple fournit une description complète du système de gestion de la mémoire de Cocoa dans Guide de programmation de gestion de la mémoire pour Cocoa, à la fin duquel se trouve un résumé bref mais précis de Règles de gestion de la mémoire.

Je n'ajouterai pas de détails sur la conservation/la publication, sauf que vous voudrez peut-être penser à perdre 50 $ et à obtenir le livre Hillegass, mais je suggérerais fortement de commencer à utiliser les outils Instruments très tôt dans le développement de votre application (même votre Premier!).Pour ce faire, Exécutez->Démarrer avec les outils de performances.Je commencerais par Leaks, qui n'est qu'un des nombreux instruments disponibles, mais qui vous aidera à vous montrer quand vous avez oublié de publier.La quantité d'informations qui vous sera présentée est assez intimidante.Mais consultez ce tutoriel pour vous lever et aller vite :
TUTORIEL CACAO :RÉPARER LES FUITES DE MÉMOIRE AVEC DES INSTRUMENTS

En fait, j'essaie de forcer les fuites pourraient être un meilleur moyen d’apprendre, en retour, à les prévenir !Bonne chance ;)

Matt Dillard a écrit:

return [[s autorelease] release];

La libération automatique fait pas conserver l'objet.La libération automatique le met simplement en file d'attente pour être publié plus tard.Vous ne voulez pas y avoir de déclaration de libération.

Ma collection habituelle d'articles sur la gestion de la mémoire Cocoa :

gestion de la mémoire du cacao

Un screencast gratuit est disponible sur le réseau iDeveloperTV

Gestion de la mémoire en Objective-C

La réponse de NilObject est un bon début.Voici quelques informations supplémentaires concernant la gestion manuelle de la mémoire (requis sur l'iPhone).

Si vous personnellement alloc/init un objet, il est livré avec un nombre de références de 1.Vous êtes responsable du nettoyage après celui-ci lorsque vous n'en avez plus besoin, soit en appelant [foo release] ou [foo autorelease].release le nettoie immédiatement, tandis que autorelease ajoute l'objet au pool de libération automatique, qui le libérera automatiquement ultérieurement.

la libération automatique est principalement destinée lorsque vous disposez d'une méthode qui doit renvoyer l'objet en question (vous ne pouvez donc pas le libérer manuellement, sinon vous renverrez un objet nul) mais vous ne voulez pas non plus vous y accrocher.

Si vous acquérez un objet pour lequel vous n'avez pas appelé alloc/init pour l'obtenir, par exemple :

foo = [NSString stringWithString:@"hello"];

mais vous voulez vous accrocher à cet objet, vous devez appeler [foo retention].Sinon, il est possible que cela devienne autoreleased et tu garderas une référence nulle (comme ce serait le cas ci-dessus stringWithString exemple).Lorsque vous n'en avez plus besoin, appelez [foo release].

Les réponses ci-dessus reformulent clairement ce que dit la documentation ;le problème auquel se heurtent la plupart des nouveaux arrivants est celui des cas sans papiers.Par exemple:

  • Libération automatique:Docs dit qu'il déclenchera une version "à un moment donné dans le futur". QUAND?!Fondamentalement, vous pouvez compter sur la présence de l'objet jusqu'à ce que vous retiriez votre code dans la boucle d'événements système.Le système PEUT libérer l'objet à tout moment après le cycle d'événements en cours.(Je pense que Matt a dit cela plus tôt.)

  • Chaînes statiques: NSString *foo = @"bar"; -- devez-vous conserver ou libérer cela ?Non.Que diriez-vous

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • La règle de création:Si vous l'avez créé, vous en êtes propriétaire et vous êtes censé le publier.

En général, la façon dont les nouveaux programmeurs Cocoa se trompent est de ne pas comprendre quelles routines renvoient un objet avec un retainCount > 0.

Voici un extrait de Règles très simples pour la gestion de la mémoire dans Cocoa:

Règles de décompte de rétention

  • Dans un bloc donné, l'utilisation de -copy, -alloc et -retain doit être égale à l'utilisation de -release et -autorelease.
  • Objets créés à l'aide de constructeurs pratiques (par ex.StringWithString de NSString) sont considérés comme publiés automatiquement.
  • Implémentez une méthode -dealloc pour libérer les variables d'instance que vous possédez

La 1ère puce dit :si tu as appelé alloc (ou new fooCopy), vous devez appeler release sur cet objet.

La 2ème puce dit :si vous utilisez un constructeur pratique et tu as besoin de l'objet pour traîner (comme pour une image à dessiner plus tard), vous devez la conserver (puis la relâcher plus tard).

Le 3ème devrait être explicite.

Beaucoup de bonnes informations sur cocoadev également :

Comme plusieurs personnes l'ont déjà mentionné, Apple Introduction à la gestion de la mémoire est de loin le meilleur point de départ.

Un lien utile que je n'ai pas encore vu mentionné est Gestion pratique de la mémoire.Vous le trouverez au milieu de la documentation Apple si vous les lisez, mais cela vaut la peine d'établir un lien direct.C'est un brillant résumé des règles de gestion de la mémoire avec des exemples et des erreurs courantes (essentiellement ce que les autres réponses tentent d'expliquer ici, mais pas aussi bien).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top