[résolu] NSColor: Problème de retain?

RocouRocou Membre
juin 2009 modifié dans API AppKit #1
Bonjour,

Mon application plante et je ne sais pas pourquoi. Je suppose que c'est un problème de release/retain mais je ne sais pas comment résoudre cela. J'ai eu exactement le même problème avec une NSDate.

Voici le topo:

Je veux dessiner des carrés de couleur dans une vue.

J'ai une classe Toto qui ne contient une variable NSColor.

Dans initWithFrame,
Je créée une instance de Toto, j'initialise son instance NSColor avec une couleur puis j'ajoute cet objet dans un tableau ([totoArray addObject:monToto])
et je release mon objet.
(je répète l'opération dans une boucle certain nombre de fois)

Puis dans drawRect,
Je créée une instance de Toto, j'initialise son instance NSColor avec le premier élément de mon tableau totoArray et je dessine mon premier carré avec pour couleur [monToto couleur]
Je répète l'opération autant de fois qu'il y a d'éléments dans le tableau.

ça fonctionne mais quand j'essaie, par exemple, d'agrandir ma fenêtre, ça plante.

Ci-joint un mini projet.

Réponses

  • NoNo Membre
    02:01 modifié #2
    Il y a un problème avec tes couleurs.

    Tu récupères un NSColor via la méthode ColorValueForHex:.
    Cet objet NSColor n'étant pas créé via un alloc/init/new/copy, donc il s'agit d'un objet en autorelease pool.

    Tu enregistres cet objet dans un objet de ta classe toto.
    Mais comme il n'y a aucun retain fait sur ce NSColor, il est donc susceptible d'être purgé de la mémoire à  tout moment.

    Modifie le @property *couleurToto de ta classe Toto en y enlevant assign et en le remplaçant par retain.
  • RocouRocou Membre
    02:01 modifié #3
    dans 1245141755:

    Il y a un problème avec tes couleurs.

    Tu récupères un NSColor via la méthode ColorValueForHex:.
    Cet objet NSColor n'étant pas créé via un alloc/init/new/copy, donc il s'agit d'un objet en autorelease pool.

    Tu enregistres cet objet dans un objet de ta classe toto.
    Mais comme il n'y a aucun retain fait sur ce NSColor, il est donc susceptible d'être purgé de la mémoire à  tout moment.


    Merci, ça confirme ce que je soupçonnais.

    dans 1245141755:

    Modifie le @property *couleurToto de ta classe Toto en y enlevant assign et en le remplaçant par retain.


    Hélas, cela ne change rien du tout  :'(
  • RocouRocou Membre
    02:01 modifié #4
    Je remarque autre chose, si dans ma méthode ColorValueForHex

    je remplace:
    return [NSColor colorWithDeviceRed:c1/255.0 green:c2/255.0 blue:c3/255.0 alpha:1];

    par

    return [NSColor redColor];

    Pas de plantage. C'est à  n'y rien comprendre.
  • RocouRocou Membre
    juin 2009 modifié #5
    Bon, je n'y arrive décidément pas.
    je vais manipuler des NSString et les convertir selon les besoins.

    EDIT: bon, ça ne fonctionne pas davantage  :-\\
    En fait, dés qu'il s'agit d'objet, ça plante.
  • CéroceCéroce Membre, Modérateur
    juin 2009 modifié #6
    Ton code est mal écrit:
    - Tu appelles la méthode -init de Toto, que tu n'as pas définie, c'est donc l'implémentation de NSObject qui est appelée. A priori, pas de problème puisque les variables d'instance sont initialisées à  zéro.

    - Ensuite tu appelles initToto:, qui n'est pas une méthode d'init, mais un équivalent de setCouleurToto: (pourquoi ne pas appeler celle-ci ?), mais sans retain (c'est mal).

    - Il manque une méthode -dealloc:
    <br />- (void) dealloc<br />{<br />[couleurToto release];<br />[super dealloc];<br />}<br />
    



    Les méthodes redColor et compagnie renvoient un singleton qui ne sera donc jamais désalloué -> pas de plantage.
  • AliGatorAliGator Membre, Modérateur
    02:01 modifié #7
    Oui sauf qu'attention, je me demande si redColor ne renvoie pas un objet constant (un peu comme @toto pour une NSString), bref une constante compilée dans le programme, et non pas un objet autoreleased créé à  chaque fois qu'on appelle [NSColor redColor]"...
    (d'ailleurs [tt]NSLog(@retainCount: %d , [[NSColor redColor] retainCount]);[/tt] pourrait nous donner plus d'infos, quoique je me méfie quand même de ce genre de test)

    Du coup ça pourrait expliquer pourquoi avec [tt]redColor[/tt] ça ne plante pas alors qu'avec [tt]colorWithDeviceRed:green:blue:alpha:[/tt] ça plante, si tu fais un release de trop sur ta couleur retournée : un release sur une constante n'ayant aucun effet (no-op)
  • RocouRocou Membre
    02:01 modifié #8
    dans 1245147112:

    Ton code est mal écrit:
    - Tu appelles la méthode -init de Toto, que tu n'as pas définie, c'est donc l'implémentation de NSObject qui est appelée. A priori, pas de problème puisque les variables d'instance sont initialisées à  zéro.


    - Il manque une méthode -dealloc:
    <br />- (void) dealloc<br />{<br />[couleurToto release];<br />[super dealloc];<br />}<br />
    


    Oui mais j'ai écris ce petit projet à -la-va-vite pour illustrer mon problème.
    Tu as bien sûr raison sur la rigueur à  laquelle il faudrait que je m'astreigne mais tout ceci ne résout pas mon problème  :(

    dans 1245147112:

    - Ensuite tu appelles initToto:, qui n'est pas une méthode d'init, mais un équivalent de setCouleurToto: (pourquoi ne pas appeler celle-ci ?), mais sans retain (c'est mal).


    La solution est-elle ici? Comment faire ce 'retain'? A quel niveau du code?



    dans 1245147112:

    Les méthodes redColor et compagnie renvoient un singleton qui ne sera donc jamais désalloué -> pas de plantage.

    Faudrait-il que je code moi-même un 'singleton'?
  • RocouRocou Membre
    juin 2009 modifié #9
    dans 1245148308:

    Oui sauf qu'attention, je me demande si redColor ne renvoie pas un objet constant (un peu comme @toto pour une NSString), bref une constante compilée dans le programme, et non pas un objet autoreleased créé à  chaque fois qu'on appelle [NSColor redColor]"...
    (d'ailleurs [tt]NSLog(@retainCount: %d , [[NSColor redColor] retainCount]);[/tt] pourrait nous donner plus d'infos, quoique je me méfie quand même de ce genre de test)

    Du coup ça pourrait expliquer pourquoi avec [tt]redColor[/tt] ça ne plante pas alors qu'avec [tt]colorWithDeviceRed:green:blue:alpha:[/tt] ça plante, si tu fais un release de trop sur ta couleur retournée : un release sur une constante n'ayant aucun effet (no-op)

    N'y-a-t-il pas un moyen de faire en sorte que certains objets ne soient jamais releasés?

    Ce que je ne comprends pas, c'est que je stocke tout dans un tableau. Comment le contenu des objets peut-il disparaà®tre? Le tableau ne stocke que des pointeurs sur des objets?

    Bon, je vais finir par utiliser des types C...
  • CéroceCéroce Membre, Modérateur
    02:01 modifié #10
    Les propriétés introduites avec ObjC 2.0 sont bien pratiques, mais il faut comprendre ce qu'elles impliquent.

    Assign

    @property (assign) NSColor* couleur;
    


    le @synthesize couleur; est équivalent au code suivant:

    <br />- (NSColor*) couleur<br />{<br />	return couleur;<br />}<br /><br />- (void) setCouleur:(NSColor*)newCouleur<br />{<br />	couleur = newCouleur;<br />}
    


    -> On conserve seulement une référence vers newCouleur. Si newCouleur est désallouée et qu'on tente d'y accéder => plantage.


    Retain

    @property (retain) NSColor* couleur;
    


    le @synthesize couleur; est équivalent au code suivant:

    <br />- (NSColor*) couleur<br />{<br />	return couleur;<br />}<br /><br />- (void) setCouleur:(NSColor*)newCouleur<br />{<br />	[couleur release];<br />	couleur = [newCouleur retain];<br />}
    


    -> On conserve la référence sur la nouvelle couleur, et on incrémente son retainCount. => Il faut libérer la mémoire occupée par couleur dans -dealloc.
  • RocouRocou Membre
    juin 2009 modifié #11
    dans 1245155224:

    Les propriétés introduites avec ObjC 2.0 sont bien pratiques, mais il faut comprendre ce qu'elles impliquent.

    Céroce ton message est très clair et je t'en remercie, cela confirme ce que j'avais parfaitement compris (pour une fois). Cependant, si tu reprends mon code et que tu le modifies comme tu viens de me l'écrire, ça plante toujours autant.

    EDIT: ha ok, comme ça, c'est mieux  :)

    Ouf, ça fonctionne, merci!
    Et mon ancien problème de NSDate est résolu également
  • CéroceCéroce Membre, Modérateur
    juin 2009 modifié #12
    Le pire, c'est que ton programme d'origine ne plante pas sur ma machine ! Pour le coup, ça c'est vraiment embêtant.
Connectez-vous ou Inscrivez-vous pour répondre.