Initialiser une variable de classe

FloFlo Membre
00:51 modifié dans API AppKit #1
Bonjours à  tous !

Je patauge un petit peu avec mes variables de classes :
<br />+ (void) initialize<br />{<br />	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />	<br />&nbsp; &nbsp; if ( self == [ITDataGraph class] ) <br />	{<br />		undoManager = [[NSUndoManager alloc] init];<br />		<br />		NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);<br />		NSString *basePath = ([paths count] &gt; 0) ? [paths objectAtIndex: 0] : NSTemporaryDirectory();<br />		<br />		dataFolder = [basePath stringByAppendingPathComponent: DATA_FOLDER];<br />		dataFile = [dataFolder stringByAppendingPathComponent: DATA_FILE];<br />&nbsp; &nbsp; }<br />	<br />	[pool drain];<br />}<br />


Mes variables de classe sont déclarées en static et j'ai des messages d'erreur à  l'execution du type :


2009-02-19 11:12:53.004 iTrend[597:10b] *** _NSAutoreleaseNoPool(): Object 0x10a340 of class ITDataGraph autoreleased with no pool in place - just leaking
Stack: (0x9342e73f 0x9333ae32 0x25c2 0x285e 0x9515abdf 0x951540d3 0x8fe02e38 0x8fe0e7cf 0x8fe0e8c9 0x8fe04102 0x8fe07bcf 0x8fe01872 0x8fe01037)


Quelqu'un aurait-il une explication ?
«1

Réponses

  • schlumschlum Membre
    février 2009 modifié #2
    ça me paraà®t sacrement bizarre comme code...
    Pourquoi avoir plein de variables statiques comme ça ? C'est super crade  :o
  • schlumschlum Membre
    00:51 modifié #3
    En tout cas pour le message d'erreur, ça n'a rien à  voir avec ce code... Il n'y a pas d'allocation d'objet ITDataGraph dedans.
  • FloFlo Membre
    février 2009 modifié #4
    Ben non, ce sont des variables de classe et je me sert de la méthode +(void)initialize pour les initialiser comme dit dans la doc Apple. Elles sont déclarées en static dans le fichier .m de ma classe.

    Par ce procédé, j'entends partager ces variables avec chaque instance de la classe comme des vrais variables de classe quoi ! Ne serait-ce pas possible en objective-c ?

  • schlumschlum Membre
    00:51 modifié #5
    Mais qu'est ce que genre de choses vient faire en variables de classes ? C'est sensé être des variables classiques d'un contrôleur quoi...  ???
    (au pire des singletons...)
  • FloFlo Membre
    00:51 modifié #6
    Ben pourquoi pas ? Je  souhaite tout simplement que chaque instance de la classe partage le même NSUndoManager sans avoir à  créer un controller spécialement pour ça...
  • FloFlo Membre
    00:51 modifié #7
    Je vais préciser les choses, j'ai fais ça comme ça :
    <br />static ITDataGraph		*entry;<br />static NSUndoManager	*undoManager;<br /><br />static NSString			*dataFile;<br />static NSString			*dataFolder;			<br /><br /><br />@implementation ITDataGraph<br /><br />+ (void) initialize<br />{<br />	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />	<br />&nbsp; &nbsp; if ( self == [ITDataGraph class] ) <br />	{<br />		NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);<br />		NSString *basePath = ([paths count] &gt; 0) ? [paths objectAtIndex: 0] : NSTemporaryDirectory();<br />		<br />		undoManager = [[NSUndoManager alloc] init];<br />		dataFolder = [basePath stringByAppendingPathComponent: DATA_FOLDER];<br />		dataFile = [dataFolder stringByAppendingPathComponent: DATA_FILE];<br />&nbsp; &nbsp; }<br />	<br />	[pool drain];<br />}<br /><br />@end<br />
    


    Des propos d'Apple, on peut même mettre toutes les variables d'une classe en variable de classe si cette dernière ne doit être instanciée qu'une fois, alors je vois pas trop en quoi ce que je suis en train de faire est si monstrueux...
  • schlumschlum Membre
    00:51 modifié #8
    Dans ce cas, quitte à  faire un truc static, on le fait proprement avec un singleton  :P
    http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html
  • schlumschlum Membre
    00:51 modifié #9
    dans 1235041495:
    Des propos d'Apple, on peut même mettre toutes les variables d'une classe en variable de classe si cette dernière ne doit être instanciée qu'une fois, alors je vois pas trop en quoi ce que je suis en train de faire est si monstrueux...


    Parce que c'est pas du tout MVC et pas du tout Thread safe (et pas du tout esprit Cocoa).

    Où est-ce qu'Apple parle de ça ?
  • FloFlo Membre
    00:51 modifié #10
    J'ai lu ça dans la doc mais je suis infichu de retrouver où.
    Mais bon vu la levée de bouclier je crois que je vais me raviser...
  • schlumschlum Membre
    00:51 modifié #11
    Ben la levée de boucliers c'est juste moi pour l'instant  :)

    Sinon, je reste persuadé que le message de "leak" n'a rien à  voir avec ce code précis.
    Où est le code ou tu appelles un "autorelease" sur un objet ITDataGraph ? (parce que c'est pas le cas dans ce code !)
  • FloFlo Membre
    00:51 modifié #12
    Ben pour l'instant c'est bien le problème, il n'y a aucun endroit ou je fais un autorelease sur un objet ITDataGraph, il s'agit juste de la classe qui contient la méthode initialize que j'ai écrite plus haut...
  • tabliertablier Membre
    00:51 modifié #13
    Si j'en crois la doc d'O'REILLY, Il n'existe pas de variable de classe en Objective-C. En Objective-C++ je ne sais pas! Néanmoins, avoir une variable accessible par tout les objets d'un même programme est souvent une facilité. Il me semble qu'il y a déja eu un post la-dessus et que bru proposait des solutions pour faire cela.
  • FloFlo Membre
    00:51 modifié #14
    Ben justement c'est après lecture de ce post que j'ai entrepris de faire ce que je suis en train de faire...

    Le problème c'est qu'un autorelease est envoyé à  ITDataGraph alors que pour l'instant je n'instantie pas du tout cette classe. J'ai des héritier mais aucune déclaration explicite de variable de type ITDataGraph...
  • schlumschlum Membre
    00:51 modifié #15
    Essaie de mettre un point d'arrêt sur "-[ITDataGraph autorelease]"
  • schlumschlum Membre
    00:51 modifié #16
    Un autre truc m'étonne... à  quoi sert "if ( self == [ITDataGraph class] )" ?
    Si c'est une méthode de classe de ITDataGraph, c'est évident que "self==[ITDataGraph class]", non ?  ???
  • AliGatorAliGator Membre, Modérateur
    février 2009 modifié #17
    Hé non schlum, il me semble qu'on en a parlé dans un autre post de cette subtilité...
    Je crois que c'est un truc à  cause des méthodes virtuelles, genre si tu as une classe B qui dérive de ta classe A et que tu appelles [super initialize] dedans ou que le initialize n'est pas défini... ça va appeler celui de la classe parent, donc A, et du coup le initialize de A va être appelé une fois pour A une fois pour B...
    D'où le test.

    [EDIT] J'ai retrouvé le post où Bru explique ça, c'est ici
  • mpergandmpergand Membre
    00:51 modifié #18
    shlum sort d'hibernation  :-*

    http://www.objective-cocoa.org/forum/index.php?topic=694.msg32557#msg32557

    <br />+ (void) initialize<br />{<br />	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />	<br />&nbsp; &nbsp; if ( self == [ITDataGraph class] ) <br />	{<br />		...<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />	<br />	[pool drain];<br />}<br /><br />@end<br /><br />
    


    [pool release] ?

    sinon à  quoi sert le pool ici ? c'est du multi-threads ?
  • schlumschlum Membre
    00:51 modifié #19
    dans 1235047660:

    Hé non schlum, il me semble qu'on en a parlé dans un autre post de cette subtilité...
    Je crois que c'est un truc à  cause des méthodes virtuelles, genre si tu as une classe B qui dérive de ta classe A et que tu appelles [super initialize] dedans ou que le initialize n'est pas défini... ça va appeler celui de la classe parent, donc A, et du coup le initialize de A va être appelé une fois pour A une fois pour B...
    D'où le test.

    [EDIT] J'ai retrouvé le post où Bru explique ça, c'est ici


    Effectivement, j'ai pensé à  ce cas en voyant le test... mais ça n'est valable que pour les classes qu'on souhaite sous-classer que je sache, non ?  ???

    Edit : je me suis posé la même question pour l'auto-release pool...

  • FloFlo Membre
    février 2009 modifié #20

    Effectivement, j'ai pensé à  ce cas en voyant le test... mais ça n'est valable que pour les classes qu'on souhaite sous-classer que je sache, non ?


    Yes, en fait la classe ITDataGraph me sert à  partager un certain nombre de variable à  tous les noeuds du graph qui sont aussi de type ITDataGraph. J'ai un certain nombre de classe qui héritent de ITDataGraph et qui sont par conséquent des noeuds du graph.

    En fait j'aurais du poster le lien du post de Bru dès le début, post que j'ai fait remonté ya pas longtemps dailleur...

    Pour l'autoreleasePool c'est une bête supposition de ma part, j'ai vu un message d'erreur du style NoAutoreleasePool et je me suis dit "tiens il faut que j'en créer une ?".

    En fait le problème est tout autre, j'ai eu la bêtise de nommer une des methodes de la classe ITDataGraph : + (void) load... Ne sachant pas à  ce moment qu'il s'agissait d'une super méthode je vous laisse imaginer pourquoi tous plantait.
  • schlumschlum Membre
    00:51 modifié #21
    Mais s'ils ne " partagent " que des variables statiques, pourquoi ne pas avoir délégué la gestion de ces variables à  un contrôleur (ou au delegate de l'application, ou un singleton si tu ne veux pas te colletiner une référence de contrôleur partout), et transformé ITDataGraph en Protocole ?
  • FloFlo Membre
    février 2009 modifié #22
    J'ai fais les choses comme ceci :

    <br />@interface ITDataGraph : NSObject &lt;NSCoding&gt;<br />{<br />	NSString		*identifier;<br />	NSMutableArray	*successors;<br />}<br /><br />dans le .m<br /><br />static ITDataGraph *entry;<br />static NSUndoManager *undoManager;<br /><br />
    


    Chaque instance de la classe ITDataGraph est un noeud du graph ayant un id et un tableau d'enfants. Je veux que chaque instance connaissent l'entrée du graph (ou la racine pour ceux qui préfèrent) et partagent le même undoManager. Il faut également que chaque classe de l'appli puisse accéder à  l'entrée du graph facilement.

    Pour ne pas me trimbaler des références partout, j'ai mis l'entrée du graph en static (variable de classe). Si on résume, la classe ITDataGraph a deux rôles, elle produit des instances (les noeuds du graph) et elle manage l'entrée du graph pour offrir aux autres classes des fonctionnalités partagées...

    Par exemple, n'importe qu'elle classe peut faire :
    <br /> ITDataGraph *graph = [ITDataGraph entry];<br /><br /> // ou encore<br /> [ITDataGraph loadGraph];<br /><br /> // ou alors<br /> [ITDataGraph saveGraph];<br /><br /> // enfin<br /> [ITDataGraph update];<br />
    


    De plus chaque sous classe peut enregister des actions dans l'undoManager etc...

    A cause de cette dualité ITDataGraph ne peut pas être un protocole (enfin je vois pas trop comment)...
    L'idée c'est que chaque noeud du graphe est lui-même considéré comme un graph de même que ses fils etc...
  • schlumschlum Membre
    00:51 modifié #23
    Ah OK... (pour l'entry ; pour l'undoManager, ça me paraà®t pas logique du tout qu'il soit là ...)
    Mais pourquoi le noe“ud d'un arbre a-t-il besoin de connaà®tre la racine ? ça sens le problème de conception non ?  ???
  • FloFlo Membre
    février 2009 modifié #24
    On va dire que c'est un effet de bord... ce que je voulais surtout c'est que chaque classe puisse faire [ITDataGraph entry] plutôt que de leur balader la référence de l'entrée du graph, mais je suis ouvert à  toutes propositions  ;)

    Toutes les modifications de données sont faites sur des éléments du graph donc il me semblait logique que chaque noeud ait un lien vers un undoManager pour enregistrer ses modifications. Tu l'aurais mis où toi ?

    Dans un sens, le graph représente la couche model de l'appli...
  • schlumschlum Membre
    00:51 modifié #25
    dans 1235053042:

    On va dire que c'est un effet de bord... ce que je voulais surtout c'est que chaque classe puisse faire [ITDataGraph entry] plutôt que de leur balader la référence de l'entrée du graph, mais je suis ouvert à  toutes propositions  ;)


    C'est pour ça que je parle de problème de conception... Pourquoi un noe“ud a-t-il besoin de connaà®tre la référence de l'entrée ?
    Dans un arbre classique, un noe“ud connaà®t en général ses fils ; dans certains cas son père mais je n'ai jamais vu de cas où il avait besoin de connaà®tre la racine (et de fait tout l'arbre !).

    Toutes les modifications de données sont faites sur des éléments du graph donc il me semblait logique que chaque noeud ait un lien vers un undoManager pour enregistrer ses modifications. Tu l'aurais mis où toi ?

    Dans un sens, le graph représente la couche model de l'appli...


    Oui, c'est la couche Model ; et un UndoManager fait partie de la couche Controller  :P
    Soit j'aurais fait un singleton Controller gérant le UndoManager (et permettant de gérer le multi-thread facilement).
    Soit j'aurais confié la gestion au delegate de l'application de classe Controller (s'il y a un nib) et fait une macro pour l'accès
    #define UNDOMGR [(Controller*)[NSApp delegate] undoManager]
  • FloFlo Membre
    00:51 modifié #26
    Ouais...

    Merci pour le temps passé à  m'aider, l'idée de l'appDelegate est pas mal je vais voir ça.  ::)
  • FloFlo Membre
    00:51 modifié #27

    Où est-ce qu'Apple parle de ça ?


    Je suis retombé dessus par hasard :
    http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-TPXREF118


    Static variables help give the class object more functionality than just that of a “factory” producing instances; it can approach being a complete and versatile object in its own right. A class object can be used to coordinate the instances it creates, dispense instances from lists of objects already created, or manage other processes essential to the application. In the case when you need only one object of a particular class, you can put all the object's state into static variables and use only class methods. This saves the step of allocating and initializing an instanc
  • schlumschlum Membre
    00:51 modifié #28
    Je ne vois pas le rapport avec le sujet d'origine  :P
    Ils parlent de n'utiliser que des méthodes de classe, donc oui, ça revient à  faire des fonctions et un contexte.
  • FloFlo Membre
    août 2009 modifié #29

    Je ne vois pas le rapport avec le sujet d'origine 




    Citation de: Flo le 19 février 2009, 12:04
    Des propos d'Apple, on peut même mettre toutes les variables d'une classe en variable de classe si cette dernière ne doit être instanciée qu'une fois, alors je vois pas trop en quoi ce que je suis en train de faire est si monstrueux...


    Parce que c'est pas du tout MVC et pas du tout Thread safe (et pas du tout esprit Cocoa).

    Où est-ce qu'Apple parle de ça ?


    réponse :


    In the case when you need only one object of a particular class, you can put all the object's state into static variables and use only class methods. This saves the step of allocating and initializing an instance.


    Je voulais juste remettre le morceau de doc qui parle de ça  :)
  • FloFlo Membre
    00:51 modifié #30
    Dans le même genre j'aurais une petite question toute bête, lorsque l'on créer une variable "static" dans un fichier .m, est-on obligé de l'initialiser dans la méthode +(void) initialize ou alors peut-on le faire un peu plus tard dans une méthode genre +(void) initMyClassVar ?
  • mpergandmpergand Membre
    00:51 modifié #31
    dans 1251111587:

    Dans le même genre j'aurais une petite question toute bête, lorsque l'on créer une variable "static" dans un fichier .m, est-on obligé de l'initialiser dans la méthode +(void) initialize ou alors peut-on le faire un peu plus tard dans une méthode genre +(void) initMyClassVar ?


    Tu peux le faire où tu veux, seulement faut faire gaffe  ;)

    Si tu le fais dans init, un accès direct en static:
    maVar=[MaClasse maVarStatic];

    ne passera pas par init et la variable ne sera pas initialisée.
Connectez-vous ou Inscrivez-vous pour répondre.