Problème avec un singleton

FloFlo Membre
septembre 2009 modifié dans API AppKit #1
Bonjour à  tous,

J'ai créer un singleton selon la méthode fournie par la doc d'Apple :
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32

Je créer également un petit cube bleu de la classe de mon singleton dans un fichier xib. Dans ce xib j'ai un bouton qui pointe sur une IBAction de mon singleton.

Dans ce même xib il y a également une instance de mon AppController.

Le problème est le suivant, si je fait un truc du style :

<br /><br />// Dans AppController également instancié par le fichier xib<br /><br />- (id) init<br />{<br />&nbsp;  self = [super init];<br /><br />&nbsp;  if (self) [MonManager sharedManager];<br /><br />&nbsp;  return self;<br />}<br /><br />


Si on essaye ensuite d'appuyer sur le bouton relié à  l'IBAction du singleton, ça ne fait rien  :'(
Si je commente la ligne [MonManager sharedManager], tout remarche.

Je suppose que la méthode init de l'AppController doit être appelée avant et donc instancier en premier le singleton. Or lorsque le xib créer le cube bleu en appelant la méthode init, ça doit lui renvoyer nil...

Une petite idée sur la manière d'arranger ça ?


«1

Réponses

  • AliGatorAliGator Membre, Modérateur
    23:52 modifié #2
    Je mettrais plutôt un simple  [tt]return (self = [MonManager sharedManager);[/tt] dans ton init non ?
  • FloFlo Membre
    23:52 modifié #3
    Ben en fait si tu veux, c'est juste que j'ai besoin d'accéder à  MonManager dans le code init de l'AppController.

    <br /><br />// Méthode init de l&#39;AppController<br />(id) init<br />{<br />&nbsp;  self = [super init];<br /><br />&nbsp;  if(self)<br />&nbsp;  {<br />&nbsp; &nbsp; &nbsp;  MonManager *monManager = [MonManager sharedManager];<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp;  // ici le code accédant à  monManager<br />&nbsp;  }<br /><br />&nbsp;  return self;<br />}<br />
    


    Le problème c'est que ça initialise le manager avant que le programme ne le créer avec la méthode init dans le fichier .xib. Et du coup ça fait foirer toutes les laisons que j'ai faites dans IB entre MonManager et le reste de l'interface... :(

  • FloFlo Membre
    23:52 modifié #4
    Modifier le singleton dans la methode appDidFinishLaunching: pourrait être une solution...

    C'est tout de même dommage qu'on ne puisse pas toucher au singleton tant que le xib ne l'ai pas lui-même instancié  >:(
  • AliGatorAliGator Membre, Modérateur
    23:52 modifié #5
    Ah oui pardon je croyais que le init que tu avais mis c'était le "init" de ton singleton que t'avais modifié par rapport à  ce qui se fait habituellement pour faire en sorte que le XIB le charge correctement (puisque le désarchivage d'un objet depuis un XIB appelle les méthodes initWithCoder ou initWithFrame... ou a défaut un simple init).

    Je ne vois pas trop en quoi le fait que le XIB crée ton singleton avant ou après change les choses... à  condition que tu fasses en sorte que la méthode initWithCoder de ton singleton, qui sera appelée typiquement par le XIB lors du désarchivage, fasse la mm chose que sharedInstance, à  savoir crée l'instance si elle n'existe pas déjà , sinon relâche la mémoire éventuellement allouée pour self et retourne la sharedInstance déjà  existante de ton singleton
  • zoczoc Membre
    23:52 modifié #6
    Voir la discussion intéressante à  ce sujet ici.

    La dernière contribution me semble être la meilleure façon d'avoir un singleton "dans un NIB", et qui soit également utilisable avant d'avoir chargé le NIB...
  • FloFlo Membre
    23:52 modifié #7

    Je ne vois pas trop en quoi le fait que le XIB crée ton singleton avant ou après change les choses...


    Ben dans le code fournis par Apple(sans init) apparemment ça change tout si on ne surcharge pas init (ce que je n'avais pas fait pensant que ça suffirait). Merci pour l'info 


    Voir la discussion intéressante à  ce sujet ici.


    Merci pour le lien  :), je pense que c'est ce qu'il me faut, je vais tester.

  • FloFlo Membre
    23:52 modifié #8
    ça marche, merci à  tous les deux !  :adios!:
  • Philippe49Philippe49 Membre
    23:52 modifié #9
    dans 1244162511:

    (puisque le désarchivage d'un objet depuis un XIB appelle les méthodes initWithCoder ou initWithFrame... ou a défaut un simple init).

    Non dans le cas d'un NSObject présent dans le nib, c'est init qui est appelé pas initWithCoder:. dixit NSNib
  • FloFlo Membre
    23:52 modifié #10
    Heu... ça marche plus... J'ai relancé XCode et maintenant ça ne fonctionne pas (rien n'a été changé depuis)...  :'(

    Je ne comprends plus rien... il y aurait-il une part d'aléatoire dans la manière d'instancier les éléments d'un fichier xib ?
  • FloFlo Membre
    juin 2009 modifié #11
    Je vous mets le code de mon singleton, si vous voyez quelque chose :

    <br /><br />static ITSymbolManager *sharedSymbolManager = nil;<br /><br />/* ----------------------------------------------------------------------------------------<br />	<br /> ---------------------------------------------------------------------------------------- */<br /><br />+ (ITSymbolManager *) sharedManager<br />{<br />	@synchronized (self)<br />	{<br />		if (!sharedSymbolManager)<br />			[[self alloc] init];<br />	}<br />	<br />	return sharedSymbolManager;<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br />	<br /> ---------------------------------------------------------------------------------------- */<br /><br />+ (id) allocWithZone: (NSZone *)zone<br />{<br />	@synchronized (self)<br />	{	<br />		if (!sharedSymbolManager) <br />		{	<br />			sharedSymbolManager = [super allocWithZone: zone];<br />		<br />			[(NSImage *)[NSImage imageNamed: THUMBNAIL] setName: IDENTIFIER];<br />		<br />			return sharedSymbolManager; <br />		}<br />	}<br />	<br />    return nil;<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br /> <br /> ---------------------------------------------------------------------------------------- */<br /><br />- (id) init<br />{	<br />    @synchronized ([self class])<br />    {<br />        if (!sharedSymbolManager)<br />	{<br />            if (self = [super init]) <br />	    {<br />                sharedSymbolManager = self;<br />				<br />		[(NSImage *)[NSImage imageNamed: THUMBNAIL] setName: IDENTIFIER];<br />            }<br />        }<br />   }<br />	<br />    return sharedSymbolManager;<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br /> <br /> ---------------------------------------------------------------------------------------- */<br /><br />- (id) copyWithZone: (NSZone *)zone<br />{<br />    return self;<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br /> <br /> ---------------------------------------------------------------------------------------- */<br /><br />- (id) retain<br />{<br />    return self;<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br />	Denotes an object that cannot be released.<br /> ---------------------------------------------------------------------------------------- */<br /><br />- (unsigned) retainCount<br />{<br />    return UINT_MAX;  <br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br />	Do nothing.<br /> ---------------------------------------------------------------------------------------- */<br /><br />- (void) release<br />{<br />}<br /><br /><br />/* ----------------------------------------------------------------------------------------<br /> <br /> ---------------------------------------------------------------------------------------- */<br /><br />- (id) autorelease<br />{<br />    return self;<br />}<br />
    



    Ce qui est marrant c'est que quand j'ai rajouté la méthode init ça a marché. Je relance XCode un peu plus tard et la ça craque sans qu'il n'y ai eu aucunes modifs de ma part...
  • FloFlo Membre
    23:52 modifié #12
    J'ai essayé de lancer XCode plusieurs fois (sans modifier quoi que ce soit) et des fois ça marche, des fois ça ne marche pas !

    Quand ça ne marche pas, il suffit que je commente le init de l'AppDelegate pour que ça remarche, et après si je décommente et bien ça marche ! (enfin pas tout le temps)

    [move] :'( ;D :'( ;D :'( ;D :'( ;D :'( ;D :'( ;D [/move]


    Ya de quoi devenir dingue...

  • Philippe49Philippe49 Membre
    23:52 modifié #13
    Comme cela ça a l'air de marcher. Au désarchivage du nib il est fait un [[ alloc] init]
    Le alloc fait passer par allocWithZone, donc crée le singleton
    init se contente de le renvoyer ..


    #import &quot;MySingleton.h&quot;<br /><br />static MySingleton * sharedInstance=nil;<br /><br />@implementation MySingleton<br /><br />-(id) init {<br />	NSLog(@&quot;initializing&quot;);<br />	return sharedInstance;	<br />}<br /><br /><br /><br />+(MySingleton*) sharedInstance {<br />	@synchronized(self) {<br />		if(nil==sharedInstance) {<br />			[[self alloc] init];<br />		}<br />	}<br />	return sharedInstance;	<br />}<br />+(id) allocWithZone:(NSZone*)zone {<br />	NSLog(@&quot;allocing with zone&quot;);<br />	@synchronized(self) {<br />		if(nil==sharedInstance) {<br />			sharedInstance=[super allocWithZone:zone];<br />			return sharedInstance;<br />		}<br />	}<br />	return nil;<br />}<br />- (id)copyWithZone:(NSZone *)zone	{<br />	return self;<br />}<br />- (id)retain	{<br />	return self;<br />}<br />- (unsigned)retainCount	{<br />	return UINT_MAX;&nbsp; //denotes an object that cannot be released<br />}		<br />- (void)release	{<br />	//do nothing<br />}		<br />- (id)autorelease{<br />	return self;<br />}<br /><br />@end<br />
    



    Je vais essayer d'éteindre XCode, et d'allr faire un tour pour voir si y aurait quelque chose ... ;D
  • Philippe49Philippe49 Membre
    23:52 modifié #14
    :(renaud):  :(renaud):

    Voilà  je suis revenu. Cela fonctionne. Je joins le code du mini-projet pour ceux qui voudrait faire des essais de confirmation.
  • AliGatorAliGator Membre, Modérateur
    23:52 modifié #15
    Je ferais bien quand même un [super init] dans le init, moi, non ?

    Quand on appelle la méthode sharedInstance (si on suppose qu'on met pas le singleton dans le nib, quoi mais qu'on l'appelle la première fois par code), ça fait un alloc => allowWithZone, puis un init... qui renvoie sharedInstance sans appeller le init de NSObject ? pas super propre, non ?

    Et si on fait le alloc/init nous-même (au lieu d'appeler sharedInstance), ou si on laisse le désarchivage du XIB faire ce alloc/init, bah ça fait le allocWithZone suivi du init, donc tout pareil, du coup ça c'est OK je pense.
    Mais ne pas appeler le [super init] me choque un peu...
  • Philippe49Philippe49 Membre
    23:52 modifié #16
    dans 1244216988:

    Je ferais bien quand même un [super init] dans le init, moi, non ?

    Quand on appelle la méthode sharedInstance (si on suppose qu'on met pas le singleton dans le nib, quoi mais qu'on l'appelle la première fois par code), ça fait un alloc => allowWithZone, puis un init... qui renvoie sharedInstance sans appeller le init de NSObject ? pas super propre, non ?

    On peut le faire pour faire propre, mais le init de NSObject ne fait rien.

    doc sur -(id)init
    "An object isn't ready to be used until it has been initialized. The init method defined in the NSObject class does no initialization; it simply returns self."
  • FloFlo Membre
    23:52 modifié #17
    En fait le projet que tu as envoyé ne décrit pas la situation que j'exposais, je l'ai modifié en conséquence et du coup il ne marche pas... est-ce que quelqu'un peut tester chez lui svp ?
  • FloFlo Membre
    juin 2009 modifié #18
    Enfin ça fait comme pour mon projet à  moi, des fois ça marche et d'autres pas...

    [EDIT]
    Voila ce qu'affiche le log quand ça ne marche pas :
    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing
    2009-06-05 18:29:06.635 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.641 Untitled[618:10b] allocing with zone

    Quand ça marche, il y a seulement :
    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing

    Essayez plusieurs fois jusqu'à  avoir la situation d'erreur... souvent chez moi ça survient quand on laisse XCode allumé un petit moment sans rien faire et qu'on relance la compile (pomme + R)
  • Philippe49Philippe49 Membre
    23:52 modifié #19
    Je n'arrive pas à  reproduire ce phénomène, pour l'instant cela fonctionne.
    Le fait de poser une target/action se fait normalement après les initialisations de tous les éléments du xib, donc je ne vois pas de problème de synchronisation.
    Bizarre
  • FloFlo Membre
    23:52 modifié #20
    Tu n'arrives jamais à  un log qui ressemble à  ça ?


    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing
    2009-06-05 18:29:06.635 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.641 Untitled[618:10b] allocing with zone


    Chez moi après certain build ça le fait, et l'appuie sur le bouton ne produit aucun message dans la console...
  • Philippe49Philippe49 Membre
    23:52 modifié #21
    Pas encore ... toujours


    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing

  • Philippe49Philippe49 Membre
    juin 2009 modifié #22
    Ah peut-être une piste ...
    En ajoutant un deuxième MySingleton dans le xib, j' ai le message

    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing
    2009-06-05 18:29:06.635 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.641 Untitled[618:10b] allocing with zone


    Puis en supprimant le 2ème, cela ne marche plus.
    Je peux encore supprimer le premier et là  j'ai un simple

    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing


    C'est comme si quelque part quelque chose était mémorisé ...
  • Philippe49Philippe49 Membre
    23:52 modifié #23
    Mais en reprenant un projet "propre" cela remarche.
  • Philippe49Philippe49 Membre
    23:52 modifié #24
    C'est ton appel dans AppController de [MySingleton sharedInstance] qui ne va pas. Tu te mets en conflit avec la synchronisation des initialisations.
    Les initialisations d'un objet présent dans le nib se font en général dans awakeFromNib.
  • FloFlo Membre
    23:52 modifié #25
    dans 1244224670:

    Ah peut-être une piste ...
    En ajoutant un deuxième MySingleton dans le xib, j' ai le message

    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing
    2009-06-05 18:29:06.635 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.641 Untitled[618:10b] allocing with zone


    Puis en supprimant le 2ème, cela ne marche plus.
    Je peux encore supprimer le premier et là  j'ai un simple

    2009-06-05 18:29:06.631 Untitled[618:10b] allocing with zone
    2009-06-05 18:29:06.634 Untitled[618:10b] initializing


    C'est comme si quelque part quelque chose était mémorisé ...


    Oui, c'est quand même bizarre que des fois ça marche et des fois pas sans ne rien toucher au projet...


    C'est ton appel dans AppController de [MySingleton sharedInstance] qui ne va pas. Tu te mets en conflit avec la synchronisation des initialisations.
    Les initialisations d'un objet présent dans le nib se font en général dans awakeFromNib.


    Oui c'est certain que le problème vient de là , ce que je ne comprends pas c'est pourquoi ça ne fonctionne pas malgré toutes les précautions qui sont prises...

    Bon de toute façon ce n'est pas gênant de déplacer mon code dans awakeFromNib (moins que dans appDidFinishLaunching:). C'est même plus philosophie cocoa.

    Merci d'avoir donné de ton temps et de ta patience 
  • FloFlo Membre
    23:52 modifié #26
    Me revoilà   :)

    Je n'ai pas voulu créer un nouveau sujet pour une si petite question  :)beta:

    J'ai pas mal de singletons dans mon projet qui font des traitements plutôt similaires, je me suis donc naturellement demandé s'il était possible de les faire hériter d'une même classe plus générale étant elle-même un singleton ?

    Je prés-sens fortement que la réponse est non mais qui ne tente rien n'a rien  :)
  • Philippe49Philippe49 Membre
    23:52 modifié #27
    Voilà  une question pleine d'humour. Après tout , cela semble bizarre, mais pourquoi pas ?
    A essayer, en premier lieu avec dans chaque fichier .m des classes-fille une redéfinition de la variable statique sharedInstance.

    Si tu fais des essais, tiens nous au courant, c'est rigolo  :)
  • mpergandmpergand Membre
    23:52 modifié #28
    dans 1244312322:

    Me revoilà   :)

    Je n'ai pas voulu créer un nouveau sujet pour une si petite question  :)beta:

    J'ai pas mal de singletons dans mon projet qui font des traitements plutôt similaires, je me suis donc naturellement demandé s'il était possible de les faire hériter d'une même classe plus générale étant elle-même un singleton ?

    Je prés-sens fortement que la réponse est non mais qui ne tente rien n'a rien  :)


    Perdu !

    La réponse est oui et c'est même l'avantage principal du singleton !

    Sinon autant utiliser des méthodes et des variables statiques, ça marche aussi bien  ;)
  • FloFlo Membre
    23:52 modifié #29

    Perdu !

    La réponse est oui et c'est même l'avantage principal du singleton !

    Sinon autant utiliser des méthodes et des variables statiques, ça marche aussi bien 


    Oui en effet !  :o

    J'ai fait un premier petit essai(en partant du projet de Philippe) et ça marche pas trop mal, je vous le met en pièce jointe.

    Si vous avez des remarques, des astuces ou des améliorations, je suis preneur !  :)
  • FloFlo Membre
    23:52 modifié #30
    En fait ça marche pas du tout  B)

    Quand on créer plusieurs instances de MySingleton héritant de MySuperSingleton, ça crash de partout. Je pense qu'il faut redéfinir toutes les méthodes dans MySingleton, et de toute manière il y aura un conflit pour la méthode allocWithZone: ...

    ça va être plus dur que ce que je pensais  :)
  • Philippe49Philippe49 Membre
    23:52 modifié #31
    Courage , courage  :p
    Mais c'est vrai que ce sont des questions qui au final éclaircissent beaucoup de choses.
Connectez-vous ou Inscrivez-vous pour répondre.