Problème avec un singleton

2»

Réponses

  • mpergandmpergand Membre
    23:09 modifié #32
    dans 1244558932:

    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  :)


    On va faire au plus simple:
    <br />static id&nbsp; sharedInstance = nil;<br /><br />@implementation MySuperSingleton<br /><br />- (id) init <br />{<br />	return [super init];	<br />}<br /><br />+ (id ) sharedInstance <br />{<br />	@synchronized(self) <br />	{<br />		if(!sharedInstance) <br />		{<br />			[[self alloc] init];<br />		}<br />	}<br />	return sharedInstance;	<br />}<br />...<br />
    


    <br />@implementation MySingleton<br /><br />- (id) init <br />{<br />	if(self=[super init])<br />		{<br />		// init pour la sous classe ici<br />		}<br />		<br />	return self;	<br />}<br /><br />@end<br />
    


    Si tu veux que tes singletons aient une  base commune, tu fais hériter le super singleton de cette classe:
    @interface MySuperSingleton : ClasseDeBase<br />{<br /><br />}<br /><br />+ (MySuperSingleton *) sharedInstance;<br /><br />@end<br />
    


  • FloFlo Membre
    23:09 modifié #33

    On va faire au plus simple:


    Je ne suis pas sûr de bien comprendre ce que tu souhaites faire...  ;D
    En fait la classe MySuperSingleton est déjà  la base commune de laquelle je souhaite faire hériter les autres singleton (dans l'exemple).

    A priori il n'est pas possible de ne pas redéfinir les méthodes shardeInstance, allocWithZone et init dans les sous-classes. Du coup la classe MySuperSingleton devient une sorte de classe "interface". J'ai fait un projet qui marche avec les classes MySingleton et MyOtherSingleton qui héritent de MySuperSingleton.

    Est-ce correcte selon vous ?
  • FloFlo Membre
    23:09 modifié #34
    Le code :

    MySuperSingleton : la classe parente

    <br />- (id) init <br />{<br />	if (self = [super init])<br />	{<br />	      // super init cutom code here<br />	}<br />	<br />	return self;<br />}<br /><br /><br />+ (MySuperSingleton *) sharedInstance <br />{<br />	return nil;	<br />}<br /><br /><br />+ (id) allocWithZone: (NSZone *)zone <br />{	<br />	MySuperSingleton *anInstance = [super allocWithZone: zone];<br />	<br />	// MySuperSingleton custom code here<br />	<br />	return anInstance;<br />}<br /><br /><br />- (id) copyWithZone: (NSZone *)zone	<br />{<br />	return self;<br />}<br /><br /><br />- (id) retain	<br />{<br />	return self;<br />}<br /><br /><br />- (unsigned) retainCount<br />{<br />	return UINT_MAX;  //denotes an object that cannot be released<br />}		<br /><br /><br />- (void) release	<br />{<br />	//do nothing<br />}		<br /><br /><br />- (id) autorelease<br />{<br />	return self;<br />}<br />
    


    MySingleton une classe héritière :

    <br />static MySingleton *sharedInstance = nil;<br /><br />@implementation MySingleton<br /><br />- (id) init <br />{	<br />	@synchronized([self class]) <br />	{<br />            if (!sharedInstance) <br />	 &nbsp; {<br />                  if (self = [super init]) <br />		 {<br />                       &nbsp; sharedInstance = self; <br />               &nbsp; }<br />         &nbsp; }<br />        }<br />	<br />    return sharedInstance;<br />}<br /><br /><br />+ (MySingleton *) sharedInstance <br />{<br />	@synchronized(self) <br />	{<br />		if(!sharedInstance) <br />		{<br />			[[self alloc] init];<br />		}<br />	}<br />	<br />	return sharedInstance;	<br />}<br /><br /><br />+ (id) allocWithZone: (NSZone *)zone <br />{<br />	@synchronized(self) <br />	{<br />		if(!sharedInstance) <br />		{<br />			sharedInstance = [super allocWithZone: zone];<br />			return sharedInstance;<br />		}<br />	}<br />	<br />	return sharedInstance;<br />}<br /><br />@end<br />
    


    ça marche en l'état mais ce qu'il serait bien c'est que les comportements implémentés dans allocWithZone:, init, et shardeInstance soient hérités de la super classe et pas redéfinis à  chaque fois par chaque sous-classe... Est-ce possible à  votre avis ?
  • mpergandmpergand Membre
    23:09 modifié #35
    Il était une fois un singleton qui n'en était pas  :)

  • FloFlo Membre
    23:09 modifié #36

    Il était une fois un singleton qui n'en était pas 


    Ha bon ?  :(
    Pourtant il n'existe bien qu'une seule instance pour chaque classe (Sauf la classe MySuperSingleton qui n'est pas voué à  être instancié).

    Tu pourrais être plus explicite ?
  • mpergandmpergand Membre
    23:09 modifié #37
    Est-ce que mon code correspond à  ce que tu veux faire ?

    Je ne suis pas sûr de bien comprendre ce que tu souhaites faire... 


    pareil  :)
  • FloFlo Membre
    juin 2009 modifié #38


    Je ne suis pas sûr de bien comprendre ce que tu souhaites faire...

    pareil   :)


    Je vais tenter de clarifier les choses alors !  :)
    En fait, on peut trouver dans la doc Apple le code conseillé pour implémenter un singleton style NSUserDefaults ou encore NSFileManager.

    Ce que je souhaite faire c'est UNE super classe de laquelle je peux en faire hériter plusieurs sans avoir à  ré-implémenter le comportement d'un singleton (sans re-définir les méthode allocWithZone:, sharedInstance, init....).

    Pour l'instant ça ne marche pas, je suis obliger de ré-écrire le code fournis par Apple dans chacune de mes sous-classes...
    Si tu veux c'est un peu comme si on pouvais avoir des CustomFileManager qui soient des singletons(sans redéfinitions) par la magie de l'héritage (sinon ça sert à  rien d'utiliser l'héritage  8--))

    C'est plus clair ou pas trop ?  :o
  • AliGatorAliGator Membre, Modérateur
    23:09 modifié #39
    Pourquoi tu ne fais pas comme moi un "File Template" dans Xcode te permettant de générer en 2 clics un singleton ?

    Cf l'archive jointe, à  dézipper et à  mettre à  côté des autres "File Templates" dans "/Developer/Library/Xcode/File Templates/Cocoa/" (pour les applis Mac) et dans "/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch Class/" pour l'avoir aussi pour les projets iPhone

    Tu quittes et relance Xcode (quoique je me demande si c'est nécessaire?) et quand tu es ensuite dans un de tes projets et veux créer un singleton, tu fais File -> New File et dans les templates proposés tu auras un nouveau template de fichier "Objective-C Singleton Class". Tu rentres le nom de ton fichier et Xcode fait tout le reste t'as plus rien à  taper.
  • mpergandmpergand Membre
    23:09 modifié #40
    Exemple concret:

    soit deux classes Singleton1 et Singleton2 qui hérite de Singleton.

    Singleton1 -> Singleton ->NSObject
    Singleton2 -> Singleton ->NSObject

    Je peux créer autant de sous classes que je veux, j'aurais toujours qu'une seule instance:

    id  monSingleton1=[Singleton1 sharedInstance];
    et/ou
    id  monSingleton2=[Singleton2 sharedInstance];

    Dans tous les cas monSingleton1==monSingleton2

    Si tu veux deux instances différentes, alors ce n'est plus un singleton.

    CQFD  ;)
  • FloFlo Membre
    23:09 modifié #41

    Si tu veux deux instances différentes, alors ce n'est plus un singleton.


    C'est pas faux  ???
    comment faire alors pour partager le comportement de singleton à  plusieurs classes différente ?  :why?:


    Pourquoi tu ne fais pas comme moi un "File Template" dans Xcode te permettant de générer en 2 clics un singleton ?


    Oui c'est une bonne idée  :o

    cependant j'aimerais également que mes singletons partagent des variables et des méthodes qu'ils peuvent redéfinir...

    Bon je sens que mes singletons vont devenir des classes normales comme ça il n'y aura plus de problèmes, l'implémentation de singleton ne doit pas se prêter à  toutes ces histoires de partage de méthodes, variables et comportements...

  • mpergandmpergand Membre
    23:09 modifié #42
    En prenant mon exemple MultiSingleton, tu auras une instance et une seule par sous classe de Singleton, tu peux implémenter le code commun à  ces 'singletons' dans BaseSingleton.

    Sinon la méthode d'Aligator marche très bien, j'appelle ça le multi héritage du pauvre  ???
  • AliGatorAliGator Membre, Modérateur
    23:09 modifié #43
    Pour moi si tu veux :
    - une classe abstraite (non instanciable) ParentSingleton mutualisant des variables d'instance
    - une ou plusieurs classes concrètes, qui seront des singletons, dérivant de ParentSingleton pour récupérer les variables d'instance communes

    Il suffit de :
    - ne pas implémenter sharedInstance dans ParentSingleton (et d'interdir le init si la class de self est ParentSingleton pour interdire son instanciation directe)
    - réimplémenter juste sharedInstance dans chacune des sous-classes de ParentSingleton : chacune initialisant/renvoyant la variable gloable déclarée en "static", celle qui mémorise l'instance singleton dès qu'elle est créée... puisque justement tu vas en avoir besoin d'une différente pour chacune de tes sous-classes si tu veux que chaque sous-classe soit singleton (et donc n'avoir qu'une instance de SingletonEnfant1, et une seule instance de SingletonEnfant2, mais que ces deux SingletonEnfantN ne partagent pas pour autant la même instance singleton...
  • mpergandmpergand Membre
    23:09 modifié #44
    Pour aller jusqu'au bout des idées, disont, 'tortueuses' de flo, la classe de base BaseSingleton est instanciée dans IB  :o

    On a donc MutiSingleton qui gère 3 Singletons et BaseSingleton qui contient 3 Outlets statiques, ce qui permet aux 3 singletons d'accéder aux 3 textFields.

    Résultat: on a une instance orpheline de BaseSingleton, celle instanciée au chargement du nib, mais ça tombe bien car on en a besoin pour initialiser les outlets statiques  :)

    #import &lt;Cocoa/Cocoa.h&gt;<br />#import &quot;Singleton1.h&quot;<br />#import &quot;Singleton2.h&quot;<br />#import &quot;Singleton3.h&quot;<br /><br />int main(int argc, char *argv&#91;])<br />{<br />&nbsp;  NSAutoreleasePool* pool=[[NSAutoreleasePool alloc] init];<br />&nbsp; &nbsp; [NSApplication sharedApplication];<br />&nbsp; &nbsp; [NSBundle loadNibNamed:@&quot;MainMenu&quot; owner:NSApp];<br />&nbsp; &nbsp; 	<br />	[[Singleton1 instance] methodeDeBase];<br />	[[Singleton2 instance] methodeDeBase];<br />	[[Singleton3 instance] methodeDeBase];<br />	<br />	[[Singleton1 instance] methodeSingleton];<br />	[[Singleton2 instance] methodeSingleton];<br />	[[Singleton3 instance] methodeSingleton];<br />	<br />	// c&#39;est du static, dont accessible de partout :)<br />	[[Singleton2 textField1] setStringValue:@&quot;Singleton1&quot;];<br />	[[MultiSingleton textField2] setStringValue:@&quot;Singleton2&quot;];<br />	[[BaseSingleton textField3] setStringValue:@&quot;Singleton3&quot;];<br />	<br />	[NSApp run];<br /><br />	[pool release];<br />	<br />	//return NSApplicationMain(argc,&nbsp; (const char **) argv);<br />}<br />
    

  • FloFlo Membre
    23:09 modifié #45

    Pour aller jusqu'au bout des idées, disont, 'tortueuses' de flo, la classe de base BaseSingleton est instanciée dans IB


    Comme ça c'est pas mal  :)
    Mais bon c'est vrai que c'est un peu tortueux mon histoire  :crackboom:-

    Il me passe des trucs dans le ciboulot des fois...  :fouf):

    En tous cas on s'est bien amusé non ?  :o
  • AliGatorAliGator Membre, Modérateur
    septembre 2009 modifié #46
    J'up cette discussion (suite à  un message de 6ix qui mettais qu'il se penchait sur la question) pour vous fournir le template que j'avais fait à  l'époque pour pouvoir facilement créer une nouvelle classe Singleton dans Xcode (en un clic), grâce aux "File Templates"

    En effet, Apple nous fournit dans Xcode des modèles de classes (NSView subclass, UIViewController subclass, ...) mais rien ne nous empêche de rajouter nos propres modèles.

    J'ai donc pris l'exemple d'Apple et en ai fait un copier/coller en remplaçant juste le nom en dur (Gizmo) par "FILEBASENAMEASIDENTIFIER" (j'ai pris modèle sur les pbfiletemplates existants). J'ai encapsulé ça dans un pbfiletemplate et voilà  :)


    Le principe d'installation est simple :
    • Téléchargez le fichier joint et décompressez l'archive
    • Copiez ce dossier "Objective-C Singleton.pbfiletemplate" dans vos dossiers de Templates pour Mac et pour iPhone (histoire que le template soit dispo pour les 2), à  savoir :
      • Pour MacOSX : [tt]/Developer/Library/Xcode/File Templates/Cocoa Class[/tt]
      • Pour iPhone : [tt]/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch Class[/tt]

    NB : Il est aussi possible de créer un lien symbolique (par exemple dans le répertoire des templates de l'iPhone, faire un lien pointant sur le pbfiletemplate du répertoire de OSX) pour éviter la duplication

    Apparemment, il n'est même pas nécessaire de relancer Xcode : vous pouvez dès à  présent choisir dans Xcode le menu "File > New File..." puis choisir ce nouveau template pour ajouter un singleton au projet ouvert.
    Donnez un nom à  votre fichier, il en déduira le nom de la classe singleton à  créer et fera tout tout seul :)

    [EDIT] Avec la pièce-jointe, c'est mieux ;D
  • 6ix6ix Membre
    23:09 modifié #47
    dans 1253902568:
    Téléchargez le fichier joint et décompressez l'archive

    Est-ce qu'il s'agit du fichier de ton post précédent... ?

    Concernant les singleton, je dévie un peu de la discussion abordée jusqu'ici vers un aspect plus général. En lisant différentes discussions ici ou là , deux éléments reviennent souvent:
    (1) l'utilité réelle d'un Singleton
    (2) la façon d'implémenter un Singleton

    (1)
    Le but d'un singleton est de définir une classe dont une seule instance uniquement peut être créée. Un autre avantage est de pouvoir y accéder facilement de n'importe où dans l'application, mais ce ne devrait pas être un argument en faveur de la création d'un singleton.
    L'argument est le suivant: si tout ce que l'on veut est avoir un accès partagé et facile à  une instance, il suffit de créer une méthode +[UneClasse sharedClasse] ou +[UneClasse defaultClasse] (exemple: +[NSFileManager defaultManager]).

    (2)
    Une façon d'implémenter un singleton est donnée par Apple dans sa doc. Mais elle ne fait pas forcément l'unanimité...
    Une implémentation donnée par Peter Hosey est celle donnée ci-dessous, qu'il juge plus correcte que celle d'Apple.
    <br />@interface PRHEmptySingleton: NSObject<br />{<br />}<br /><br />+ (id) sharedFramistan;<br /><br />@end<br />static PRHEmptySingleton *sharedInstance = nil;<br /><br />@implementation PRHEmptySingleton<br /><br />+ (void) initialize {<br />&nbsp; &nbsp; if (!sharedInstance) {<br />&nbsp; &nbsp; &nbsp; &nbsp; //init will assign sharedInstance for us.<br />&nbsp; &nbsp; &nbsp; &nbsp; [[self alloc] init];<br />&nbsp; &nbsp; }<br />}<br /><br />+ (id) sharedFramistan {<br />&nbsp; &nbsp; //Already set by +initialize.<br />&nbsp; &nbsp; return sharedInstance;<br />}<br /><br />+ (id) allocWithZone:(NSZone *)zone {<br />&nbsp; &nbsp; //Usually already set by +initialize.<br />&nbsp; &nbsp; if (sharedInstance) {<br />&nbsp; &nbsp; &nbsp; &nbsp; //The caller expects to receive a new object, so implicitly retain it to balance out the caller&#39;s eventual release message.<br />&nbsp; &nbsp; &nbsp; &nbsp; return [sharedInstance retain];<br />&nbsp; &nbsp; } else {<br />&nbsp; &nbsp; &nbsp; &nbsp; //When not already set, +initialize is our caller"it&#39;s creating the shared instance. Let this go through.<br />&nbsp; &nbsp; &nbsp; &nbsp; return [super allocWithZone:zone];<br />&nbsp; &nbsp; }<br />}<br /><br />- (id) init {<br />&nbsp; &nbsp; //If sharedInstance is nil, +initialize is our caller, so initialize the instance.<br />&nbsp; &nbsp; //Conversely, if it is not nil, release this instance (if it isn&#39;t the shared instance) and return the shared instance.<br />&nbsp; &nbsp; if (!sharedInstance) {<br />&nbsp; &nbsp; &nbsp; &nbsp; if ((self = [super init])) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Initialize the instance here.<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; //Assign sharedInstance here so that we don&#39;t end up with multiple instances if a caller calls +alloc/-init without going through +sharedInstance.<br />&nbsp; &nbsp; &nbsp; &nbsp; //This isn&#39;t foolproof, however (especially if you involve threads). The only correct way to get an instance of a singleton is through the +sharedInstance method.<br />&nbsp; &nbsp; &nbsp; &nbsp; sharedInstance = self;<br />&nbsp; &nbsp; } else if (self != sharedInstance) {<br />&nbsp; &nbsp; &nbsp; &nbsp; [self release];<br />&nbsp; &nbsp; &nbsp; &nbsp; self = sharedInstance;<br />&nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; return self;<br />}<br /><br />@end<br />
    


    Je serais donc intéressé à  en débattre sur un forum francophone.  :) 8--)

    Voir en complément cet article de Peter Hosey et celui-ci de BJ Homer
  • AliGatorAliGator Membre, Modérateur
    23:09 modifié #48
    Heu juste pour dire que (1) en effet j'avais oublié de joindre le fichier à  mon post ^^ c'est donc rattrapé, et (2) ah oui tiens bah en fait je l'avais déjà  posté et justement dans ce post juste qques posts plus haut... j'avais même pas vu :P Comme quoi ^^

    Pour ton alternative de code, j'ai pas le temps ce soi... heu matin (serait p'tet temps que j'aille me coucher non ?) mais je regarderai plus tard.
  • zoczoc Membre
    23:09 modifié #49
    dans 1253906642:

    Une implémentation donnée par Peter Hosey est celle donnée ci-dessous, qu'il juge plus correcte que celle d'Apple.

    Je connaissais, et je trouve son implémentation particulièrement élégante, car elle permet de se passer totalement de synchronisation, +initialize étant appelé par le runtime Objective-C de façon totalement "thread safe".
Connectez-vous ou Inscrivez-vous pour répondre.