Quels types de fuites le comptage automatique des références dans Objective-C n'empêche-t-il pas ou ne minimise-t-il pas?

235

Sur les plates-formes Mac et iOS, les fuites de mémoire sont souvent causées par des pointeurs non publiés. Traditionnellement, il a toujours été de la plus haute importance de vérifier vos allocations, copies et conservations pour vous assurer que chacun a un message de version correspondant.

La chaîne d'outils fournie avec Xcode 4.2 introduit le comptage automatique de références (ARC) avec la dernière version du compilateur LLVM , qui élimine totalement ce problème en permettant au compilateur de gérer en mémoire vos données pour vous. C'est assez cool, et cela réduit beaucoup de temps de développement inutile et banal et empêche beaucoup de fuites de mémoire négligentes qui sont faciles à réparer avec un bon équilibre de conservation / libération. Même les pools de libération automatique doivent être gérés différemment lorsque vous activez ARC pour vos applications Mac et iOS (car vous ne devriez plus allouer vos propres NSAutoreleasePool).

Mais quelles autres fuites de mémoire n'empêchent-elles pas de faire attention?

En prime, quelles sont les différences entre ARC sur Mac OS X et iOS et la récupération de place sur Mac OS X?

BoltClock
la source

Réponses:

262

Le principal problème lié à la mémoire dont vous devez toujours être conscient est la conservation des cycles. Cela se produit lorsqu'un objet a un pointeur fort vers un autre, mais l'objet cible a un pointeur fort vers l'original. Même lorsque toutes les autres références à ces objets sont supprimées, elles se maintiennent les unes aux autres et ne sont pas publiées. Cela peut également se produire indirectement, par une chaîne d'objets qui pourrait avoir le dernier de la chaîne renvoyant à un objet antérieur.

C'est pour cette raison que les qualificatifs __unsafe_unretainedet __weakpropriétaires existent. Le premier ne conservera aucun objet sur lequel il pointe, mais laisse ouverte la possibilité que cet objet s'éloigne et qu'il pointe vers une mauvaise mémoire, tandis que le second ne conserve pas l'objet et se met automatiquement à zéro lorsque sa cible est désallouée. Des deux, __weakest généralement préféré sur les plates-formes qui le prennent en charge.

Vous utiliseriez ces qualificatifs pour des choses comme les délégués, où vous ne voulez pas que l'objet conserve son délégué et mène potentiellement à un cycle.

Un autre couple de problèmes importants liés à la mémoire est la gestion des objets Core Foundation et de la mémoire allouée à l'aide malloc()de types comme char*. ARC ne gère pas ces types, uniquement les objets Objective-C, vous devrez donc toujours les gérer vous-même. Les types de Core Foundation peuvent être particulièrement délicats, car ils doivent parfois être reliés à des objets Objective-C correspondants, et vice versa. Cela signifie que le contrôle doit être transféré dans les deux sens depuis l'ARC lors du pontage entre les types CF et Objective-C. Certains mots clés liés à ce pont ont été ajoutés, et Mike Ash a une excellente description de divers cas de pontage dans sa longue rédaction ARC .

En plus de cela, il existe plusieurs autres cas moins fréquents, mais toujours potentiellement problématiques, que la spécification publiée détaille.

Une grande partie du nouveau comportement, basé sur la conservation des objets tant qu'il y a un pointeur fort vers eux, est très similaire à la récupération de place sur Mac. Cependant, les fondements techniques sont très différents. Plutôt que d'avoir un processus de ramasse-miettes qui s'exécute à intervalles réguliers pour nettoyer les objets qui ne sont plus pointés, ce style de gestion de la mémoire repose sur les règles rigides de conservation / libération que nous devons tous respecter dans Objective-C.

ARC prend simplement les tâches de gestion de mémoire répétitives que nous avons dû faire pendant des années et les décharge sur le compilateur afin que nous n'ayons plus jamais à nous en soucier. De cette façon, vous n'avez pas les problèmes d'arrêt ou les profils de mémoire en dents de scie rencontrés sur les plateformes de récupération de place. J'ai connu ces deux problèmes dans mes applications Mac récupérées et je suis impatient de voir comment ils se comportent sous ARC.

Pour en savoir plus sur la collecte des ordures par rapport à ARC, consultez cette réponse très intéressante de Chris Lattner sur la liste de diffusion Objective-C , où il énumère de nombreux avantages d'ARC par rapport à la collecte des ordures Objective-C 2.0. J'ai rencontré plusieurs des problèmes du GC qu'il décrit.

Brad Larson
la source
2
Merci pour la réponse détaillée. J'ai eu le même problème où j'ai défini un délégué sous _unsafe_unretained et j'ai fait planter mon application, puis je l'ai corrigée en changeant en forte mais maintenant il y a une fuite de mémoire. Donc, je l'ai changé en faible et fonctionne comme un charme.
chathuram
@ichathura Wow! Vous m'avez sauvé de la boue de l'ARC. J'ai rencontré le même plantage lors de l'utilisation de CMPopTipView.
Nianliang
@BradLarson: "vous n'avez pas les problèmes d'arrêt ou les profils de mémoire en dents de scie rencontrés sur les plateformes de collecte des ordures". Je m'attendrais à des profils de mémoire d'arrêt et de dent de scie plus mauvais de la récupération basée sur la portée et à des performances bien pires du comptage de références, donc j'aimerais voir une vraie comparaison.
Jon Harrop
Brad, le lien de Chris Lattner est mort . Je ne suis pas à 100% mais j'ai trouvé cet autre lien. Je pense que c'est ce que vous vouliez lier à: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
Honey
1
@Honey - Merci de l'avoir signalé. Celui que vous liez est légèrement différent, mais j'ai remplacé le lien mort par une version archivée du message d'origine. Il se trouve dans les archives de la liste de diffusion, qui devraient être disponibles quelque part, mais je vais voir si je peux trouver leur nouvel emplacement.
Brad Larson
14

ARC ne vous aidera pas avec la mémoire non ObjC, par exemple si vous avez malloc()quelque chose, vous en avez toujours besoin free().

ARC peut être trompé performSelector:si le compilateur ne peut pas comprendre ce qu'est le sélecteur (le compilateur générera un avertissement à ce sujet).

ARC générera également du code suivant les conventions de dénomination ObjC, donc si vous mélangez le code ARC et MRC, vous pouvez obtenir des résultats surprenants si le code MRC ne fait pas ce que le compilateur pense que les noms promettent.

Rayures
la source
7

J'ai rencontré des fuites de mémoire dans mon application en raison des 4 problèmes suivants:

  1. Ne pas invalider les NSTimers lors de la fermeture des contrôleurs de vue
  2. Oublier de supprimer tous les observateurs de NSNotificationCenter lors de la fermeture du contrôleur de vue.
  3. Garder de fortes références à soi dans les blocs.
  4. Utilisation de références fortes aux délégués dans les propriétés du contrôleur de vue

Heureusement, je suis tombé sur le billet de blog suivant et j'ai pu les corriger: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-l probable - culprits/

Ed-E G
la source
0

ARC ne gère pas non plus les types CoreFoundation. Vous pouvez les `` relier '' (à l'aide CFBridgingRelease()) mais uniquement si vous allez l'utiliser comme un objet Objective-C / Cocoa. Notez que CFBridgingRelease diminue simplement le nombre de retenues CoreFoundation de 1 et le déplace vers l'ARC d'Objective-C.

MaddTheSane
la source
0

Xcode 9 fournit un excellent outil pour trouver ce genre de problèmes. Il s'appelle: " Debug Memory Graph ". En l'utilisant, vous pouvez trouver votre objet divulgué par type de classe et vous pouvez voir clairement qui détient une référence forte en le libérant à partir de là résout votre problème. Il détecte également les cycles de mémoire.

Voir plus d'informations sur son utilisation

WILL K.
la source