Plantage bizarroà¯de ; un objet perd son identité

jpimbertjpimbert Membre
août 2012 modifié dans Objective-C, Swift, C, C++ #1
J'ai un méchant plantage bien bizarre, et après quelques heures de patiente enquête je me suis aperçu que certains des objets de l'application "perdent" leur identité. Ils ne sont plus reconnus comme des objets Objective-C mais leur contenu semble pourtant correct.

Lorsqu'un objet n'est plus "reconnu" l'appel d'une méthode quelconque plante (EXC_BAD_ACCESS code 1).

Ci après un exemple où je fais "po" et "p" sur l'un de ces objets.


<br />
(lldb) po materialListManager<br />
(SKWMaterialListManager *) &#036;60 = 0x07918e60 [no Objective-C description available]<br />
(lldb) p *materialListManager<br />
(SKWMaterialListManager) &#036;61 = {<br />
  (SKWPersistentListManager) SKWPersistentListManager = {<br />
	(NSObject) NSObject = {<br />
	  (Class) isa = SKWMaterialListManager<br />
	}<br />
	(NSString *) _propertyListName = 0x00050d9c @&quot;MaterialLists&quot;<br />
	(NSMutableDictionary *) _lists = 0x07c48cc0<br />
	(NSMutableSet *) _persistentLists = 0x07c4e210<br />
	(NSMutableDictionary *) _modifiedLists = 0x07a67e90<br />
  }<br />
}<br />
(lldb)<br />




Je précise que cet objet est un "singleton" créé selon les préceptes du modern Objective-C (avec dispatch_once, sous Xcode 4.4.1), et que tout fonctionne correctement si j'utilise un objet initialisé par "new" ; malheureusement j'ai vraiment besoin d'un singleton sur ce coup là .

Lorsque le singleton est pourri, plusieurs objets à  l'intérieur de celui-ci se pourrissent aussi (pas tous).



Quelqu'un a déjà  rencontré ce problème ? Et l'aurait résolu ?

Quelqu'un peut m'aider à  aller plus loin dans mon enquête ? j'aimerais bien obtenir une alerte ou un genre de breakpoint lorsque une zone mémoire (celle qui contient mon objet) est modifiée, mais je n'ai pas trouvé comment faire sous lldb.



Je ne peux pas rester trop longtemps sur ce problème, alors je vais transformer toutes mes méthodes en méthodes de classe pour simuler un singleton. Mais je n'aime pas trop laisser un point obscur derrière moi.

Réponses

  • AliGatorAliGator Membre, Modérateur
    Il est clair qu'il s'agit d'un écrasement mémoire ou plus probablement même d'un objet pas retenu et désalloué donc trop tôt à  ton goût.



    1) Utilises-tu ARC ou non ?

    2) Quel est ton niveau en gestion mémoire ?

    3) As-tu essayé d'activer les Zombies (edites ton scheme avec le menu Product -> Edit Schemes, et dans le scheme de Debug, va dans l'onglet "Diagnostics" pour activer les zombies)

    4) Pour mettre un watchpoint sur une variable globale avec lldb, pour qu'il s'arrête quand la variable est modifiée (-w write), la commande c'est "[font=Verdana, Geneva, Arial, Helvetica, sans-serif]watchpoint set variable -w write global_var[/font]". Si c'est pour mettre sur autre chose qu'une variable globale, le plus simple est de récupérer à  un moment l'adresse mémoire que tu veux surveiller et utiliser la commande "[font=Verdana, Geneva, Arial, Helvetica, sans-serif]watchpoint set expression -w write -- my_addr[/font]" en remplaçant my_addr par l'adresse à  surveiller en écriture



    Avec les Zombies ceci dit tu devrais trouver plus facilement ton erreur, à  savoir où l'objet a été désalloué avant que tu ne tentes de le réutiliser.
  • 1) J'utilise ARC, ce qui est parfois vicieux d'ailleurs car du coup il n'y a plus de plantage lorsqu'on utilise un objet weak désalloué

    2) En principe je n'ai pas de souci mémoire, je passe régulièrement un coup de Leaks pour vérifier mais j'ai rarement des soucis

    3) J'ai activé les zombies mais j'ai toujours un plantage (pas au même endroit cette fois, mais sur le même appel de méthode sur le même objet)

    4) Je vais explorer le coup des watchpoints ça me parait très prometteur ; je te tiens au courant
  • Après moulte errances j'ai un peu avancé (c'est magique les watchpoints, même si c'est limité).
    1. J'ai mis un breakpoint à  l'initialisation de mon Singleton, puis là  j'ai affiché la description de sa principale propriété (po _lists) ; un dictionnaire mutable dont les clés sont des strings et qui contient des tableaux mutables d'objets
    2. La description de la propriété _lists me donne les adresses des objets contenus. J'affiche la description de l'une des propriétés (po [0x79eb7c0 helpText]), celle dont l'accès va provoquer un plantage plus loin. J'obtiens son adresse.
    3. Je mets un watchpoint sur cette propriété (une NSString)

    <br />
    (lldb) watchpoint set expression -w write -- 0x079edb40<br />


    Après avoir relancé le bazar, pour trouver le méchant qui me la modifie subrepticement : tiny_free_list_add_ptr

    Une fonction qui apparemment est impliquée dans la gestion du tas ; la piste du problème de gestion mémoire semble se confirmer ...

    Et ça ne me permet pas trop d'avancer car la pile est vide à  ce moment là  dans le thread principal (UIApplicationMain, c'est tout).



    Truc vraiment bizarre, ce Singleton est un bête (pas si bête que ça en fait) container initialisé avec une property list. Il contient principalement un NSMutableDictionary qui contient des NSMutableArray. Il contient des fonctions qui permettent de modifier des listes existantes ou d'en ajouter de nouvelles (il faut bien utiliser les mutables !) mais ces fonctions ne sont pas du tout utilisées entre l'initialisation de la liste et le plantage. Les objets contenus sont donc tous retenus, et en ARC je ne peux pas faire de release.



    En fait le plantage a lieu dans les tests unitaires, chaque test fait moins de 10 lignes de code. L'environnement n'est donc pas trop compliqué à  maà®triser. Le premier test passe sans encombre, au setUp de mon deuxième test le Singleton est tout pourri et le test plante (EXC_BAD_ACCESS).



    Je continue l'enquête.
  • PUTAIN DE CON !!!!!! image/ph34r.png' class='bbc_emoticon' alt='' /> image/blink.png' class='bbc_emoticon' alt=' :o ' /> image/wacko.png' class='bbc_emoticon' alt=' :* ' /> image/crybaby.gif' class='bbc_emoticon' alt=' :'( ' />

    Ma propriété helpText était assign au lieu de strong. ça fait des semaines que c'est comme ça et ça marchait très bien car cette propriété prend toujours une String constante comme valeur, mais pas dans ces nouveaux tests de mon nouveau Singleton. Je regardais pas au bon endroit !



    Merci Ali de m'avoir mis sur la piste. Et puis ça m'a au moins permis d'apprendre à  utiliser les watchpoints, ça me resservira certainement.
  • AliGatorAliGator Membre, Modérateur
    Ahah comme quoi c'est vraiment utile et important de faire des tests unitaires !
Connectez-vous ou Inscrivez-vous pour répondre.