Le Singleton est sans doute la Design Pattern la plus connue, ne serait-ce parce qu’elle est simple à comprendre. L’idée est que certains objets n’existent qu’à un seul exemplaire dans une application. Considérez par exemple ces méthodes de classes Cocoa:
- +[NSFileManager defaultManager]
- +[NSFontPanel sharedFontPanel]
- +[NSApplication sharedApplication]
Chacune renvoie un objet partagé — une instance unique pour toute l’application. Il faut appeler cette méthode de classe pour accéder à cet objet, et ne pas instancier un objet comme on le fait d’habitude, avec le couple [[alloc] init].
Le Singleton: Version simple
Créer un objet singleton en Objective-C n’est pas compliqué:
Pour le .h:
@interface MonSingleton: NSObject { } + (MonSingleton *) sharedSingleton; @end
Pour le .m:
@implementation static MonSingleton *sharedSingletonInstance = nil; - (id) init { if(self = [super init]) { // Initialisations classiques } return self; } + (MonSingleton *) sharedSingleton { if(sharedSingletonInstance == nil) // Pas encore instancié sharedSingletonInstance = [[MonSingleton alloc] init]; return sharedSingleton; } @end
Le Singleton: Version Apple
Voici l’implémentation proposée par Apple :
static MyGizmoClass *sharedGizmoManager = nil; + (MyGizmoClass*)sharedManager { if (sharedGizmoManager == nil) { sharedGizmoManager = [[super allocWithZone:NULL] init]; } return sharedGizmoManager; } + (id)allocWithZone:(NSZone *)zone { return [[self sharedManager] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (NSUInteger)retainCount { return NSUIntegerMax; //denotes an object that cannot be released } - (void)release { //do nothing } - (id)autorelease { return self; }
La différence est que les méthodes en rapport avec la création, la copie et la destructions des instances sont supplantées pour vous empêcher de créer une instance supplémentaire , ou de détruire l’instance partagée. Il s’agit d’une approche plus paranoïaque que celle que je vous proposais plus haut. Je dirais qu’elle convient mieux si vous travaillez en équipe ou que vous rendez publique votre bibliothèque de classes. La première version possède l’avantage de la simplicité, et convient pour un petit projet, par exemple sur iPhone.
4 replies on “Singleton”
Bonjour tu pourrais tout simplement faire ceci
<code>
+ (void)initialize
{
// initialize is called once per thread
// here the workaround to avoid multi-initialization
static BOOL multi_thread_init = NO;
if(!multi_thread_init)
{
multi_thread_init = YES;
sharedInstance = [[AnObject alloc] init];
}
}
</code>
le probleme dans les deux solutions c’est qu’il n y a aucun lock, on pourrait meme imaginer un meta singleton object et a chaque fois que tu creer un singleton tu renseignes le placeholder.
J’ai posté un peu vite…, donc l’idée serait de pouvoir creer une representation singleton de n’importe quel class au lieu de polluer son development par la creation specifique d’une class singletonized, je n’aborde pas les bindings liées a tel objet 😎
Bien vu, Tatouille.
Effectivement, je n’ai pas abordé la question de la concurrence ici.
On peut imaginer un problème si deux threads appellent quasi-simultanément +[sharedSingleton] alors que le singleton n’est pas encore instancié. Dans ce cas:
– la première évaluation de if(sharedSingletonInstance == nil) est vraie, un premier objet va donc être instancié.
– si au même moment, la méthode est appelée, alors f(sharedSingletonInstance == nil) sera encore évaluée à vraie, ce qui causera l’instanciation d’un deuxième objet.
Idéalement, si je ne me trompe pas, il suffirait donc mettre un lock avant l’évaluation de sharedSingletonInstance et le retirer de suite.
GCD est une solution beaucoup plus élégante de créer un singleton.
+ (MyClass *)sharedInstance {
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}