Ce que vous avez est bien, bien que vous puissiez déplacer la déclaration de variable globale dans votre méthode d'instance + (le seul endroit où elle doit être utilisée, à moins que vous ne l'autorisiez également à être définie) et utiliser un nom comme + defaultMyClass ou + sharedMyClass pour votre méthode. + l'instance n'est pas révélatrice d'intentions.
Chris Hanson
Puisqu'il est peu probable que la «réponse» à cette question changera de si tôt, je place un verrou historique sur la question. Deux raisons 1) Beaucoup de vues, votes et bon contenu 2) Pour empêcher le yo-yoing de l'ouvert / fermé. C'était une excellente question pour l'époque, mais les questions de ce type ne conviennent pas à Stack Overflow. Nous avons maintenant Code Review pour vérifier le code de travail. Veuillez prendre toute discussion de cette question à cette méta-question .
George Stocker
Réponses:
207
Une autre option consiste à utiliser la +(void)initializeméthode. De la documentation:
Le runtime envoie initializeà chaque classe d'un programme exactement une fois juste avant que la classe, ou toute classe qui en hérite, n'envoie son premier message depuis le programme. (Ainsi, la méthode ne peut jamais être invoquée si la classe n'est pas utilisée.) Le runtime envoie le initializemessage aux classes de manière thread-safe. Les superclasses reçoivent ce message avant leurs sous-classes.
Vous pourriez donc faire quelque chose de semblable à ceci:
Si le runtime ne l'appellera qu'une seule fois, que fait le BOOL? Est-ce une précaution si quelqu'un appelle explicitement cette fonction à partir de son code?
Aftermathew
5
Oui, c'est une précaution car la fonction peut également être appelée directement.
Robbie Hanson
33
Cela est également requis car il peut y avoir des sous-classes. S'ils ne remplacent pas +initializeleur super-classe, l'implémentation sera appelée si la sous-classe est d'abord utilisée.
Sven
3
@Paul, vous pouvez remplacer la releaseméthode et la rendre vide. :)
4
@aryaxt: D'après les documents répertoriés, c'est déjà thread-safe. Ainsi, l'appel est une fois par runtime - période. Cela semblerait être la solution correcte, thread-safe et efficace de manière optimale.
C'est tout ce que vous devez généralement utiliser pour les singletons. Entre autres choses, garder vos classes séparément instanciables les rend plus faciles à tester, car vous pouvez tester des instances distinctes au lieu d'avoir un moyen de réinitialiser leur état.
Chris Hanson
3
Stig Brautaset: Non, ce n'est pas correct de laisser de côté le @synchronized dans cet exemple. Il est là pour gérer la condition de concurrence possible de deux threads exécutant cette fonction statique en même temps, dépassant tous les deux le test "if (! SharedSingleton)" en même temps, et entraînant ainsi deux [MySingleton alloc] s. .. Le @synchronized {scope block} force ce second thread hypothétique à attendre que le premier thread quitte le {scope block} avant d'être autorisé à y accéder. J'espère que ça aide! =)
MechEthan
3
Qu'est-ce qui empêche quelqu'un de créer sa propre instance de l'objet? MySingleton *s = [[MySingelton alloc] init];
lindon fox
1
@lindonfox Quelle est la réponse à votre question?
Raffi Khatchadourian
1
@Raffi - désolé, je pense que j'ai dû oublier de coller ma réponse. Quoi qu'il en soit, j'ai eu le livre Pro Objective-C Design Patterns for iOSet il explique comment on fait un singelton "strict". Fondamentalement, puisque vous ne pouvez pas rendre les méthodes de lancement privées, vous devez remplacer les méthodes d'allocation et de copie. Donc, si vous essayez de faire quelque chose comme cela, [[MySingelton alloc] init]vous obtiendrez une erreur d'exécution (mais pas une erreur de compilation malheureusement). Je ne comprends pas comment tous les détails de la création d'objet, mais vous implémentez + (id) allocWithZone:(NSZone *)zonece qui s'appelle insharedSingleton
lindon fox
59
Selon mon autre réponse ci-dessous, je pense que vous devriez faire:
Ne vous embêtez pas avec tout ce que vous faites ci-dessus. Faites vos singletons (espérons-le extrêmement peu) séparément instanciables, et ayez juste une méthode partagée / par défaut. Ce que vous avez fait n'est nécessaire que si vous voulez vraiment, vraiment, UNIQUEMENT une seule instance de votre classe. Ce que vous ne faites pas, esp. pour les tests unitaires.
Chris Hanson
Le truc, c'est que c'est l'exemple de code Apple pour "créer un singleton". Mais oui, vous avez absolument raison.
Colin Barrett
1
L'exemple de code Apple est correct si vous voulez un "vrai" singleton (c'est-à-dire un objet qui ne peut être instancié qu'une fois, jamais) mais comme le dit Chris, c'est rarement ce que vous voulez ou avez besoin alors qu'une sorte d'instance partagée configurable est ce que vous veulent généralement.
Luke Redpath
Voici une macro pour la méthode ci-dessus: gist.github.com/1057420 . C'est ce que j'utilise.
Kobski
1
Mis à part les tests unitaires, il n'y a rien contre cette solution, n'est-ce pas? Et c'est rapide et sûr.
LearnCocos2D
58
Depuis que Kendall a publié un singleton threadsafe qui tente d'éviter les coûts de verrouillage, j'ai pensé que j'en jetterais un aussi:
Cas rapide: En exécution normale sharedInstancea déjà été définie, donc la whileboucle n'est jamais exécutée et la fonction revient après avoir simplement testé l'existence de la variable;
Cas lent: S'il sharedInstancen'existe pas, alors une instance est allouée et copiée dedans à l'aide d'un Compare And Swap ('CAS');
Cas envisagé: si deux threads tentent tous les deux d'appeler sharedInstanceen même temps ETsharedInstance n'existent pas en même temps, ils initialiseront tous les deux de nouvelles instances du singleton et tenteront de le mettre en position CAS. Celui qui gagne le CAS retourne immédiatement, celui qui perd libère l'instance qu'il vient d'allouer et retourne le (maintenant défini) sharedInstance. Le single OSAtomicCompareAndSwapPtrBarrieragit à la fois comme barrière d'écriture pour le thread de configuration et comme barrière de lecture depuis le thread de test.
Il s'agit d'une surpuissance totale pour au maximum une fois pendant la durée de vie d'une application. Néanmoins, il est correct, et la technique de comparaison et d'échange est un outil utile à connaître, donc +1.
Steve Madsen
Bonne réponse - la famille OSAtomic est une bonne chose à savoir
Bill
1
@Louis: Réponse incroyable et vraiment éclairante! Une question cependant: que devrait faire ma initméthode dans votre approche? Jeter une exception quand sharedInstanceest initialisé n'est pas une bonne idée, je crois. Que faire alors pour empêcher l'utilisateur d'appeler initdirectement plusieurs fois?
matm
2
Je ne l'empêche généralement pas. Il y a souvent des raisons valables pour permettre à ce qui est généralement un singleton de se multiplier, le plus commun étant pour certains types de tests unitaires. Si je voulais vraiment appliquer une seule instance, j'aurais probablement la vérification de la méthode init pour voir si le global existait, et si c'était le cas, je le fais se libérer et retourner le global.
J'ai remarqué que clang se plaint d'une fuite si vous n'affectez pas le résultat de [[self alloc] init]à sharedInst.
pix0r
Subvertir init comme celui-ci est une approche assez moche de l'OMI. Ne jouez pas avec init et / ou la création réelle de l'objet. Si vous optez plutôt pour un point d'accès contrôlé à une instance partagée, alors que vous n'insérez pas singleton dans l'objet, vous aurez plus de plaisir plus tard si vous écrivez des tests, etc. Les singletons durs sont beaucoup trop utilisés.
La documentation Apple vous recommande de vérifier le type de classe dans votre bloc d'initialisation. Parce que les sous-classes appellent l'initialisation par défaut. Il existe un cas non évident où des sous-classes peuvent être créées indirectement via KVO. Car si vous ajoutez la ligne suivante dans une autre classe:
J'ai une variation intéressante sur sharedInstance qui est thread-safe, mais ne se verrouille pas après l'initialisation. Je n'en suis pas encore assez sûr pour modifier la première réponse comme demandé, mais je la présente pour une discussion plus approfondie:
// Volatile to make sure we are not foiled by CPU cachesstaticvolatileALBackendRequestManager*sharedInstance;// There's no need to call this directly, as method swizzling in sharedInstance// means this will get called after the singleton is initialized.+(MySingleton*)simpleSharedInstance
{return(MySingleton*)sharedInstance;}+(MySingleton*)sharedInstance
{@synchronized(self){if(sharedInstance == nil){
sharedInstance =[[MySingleton alloc] init];// Replace expensive thread-safe method // with the simpler one that just returns the allocated instance.
SEL origSel =@selector(sharedInstance);
SEL newSel =@selector(simpleSharedInstance);Method origMethod = class_getClassMethod(self, origSel);Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);}}return(MySingleton*)sharedInstance;}
+1 c'est vraiment intrigant. Je pourrais utiliser class_replaceMethodpour me transformer sharedInstanceen clone de simpleSharedInstance. De cette façon, vous n'aurez plus jamais à vous soucier d'acquérir une @synchronizedserrure.
Dave DeLong
C'est le même effet, utiliser exchangeImplementations signifie qu'après init lorsque vous appelez sharedInstance, vous appelez vraiment simpleSharedInstance. En fait, j'ai commencé avec replaceMethod, mais j'ai décidé qu'il valait mieux changer les implémentations pour que l'original existe toujours si nécessaire ...
Kendall Helmstetter Gelner
Lors de tests supplémentaires, je n'ai pas réussi à faire remplacer replaceMethod - dans les appels répétés, le code appelait toujours l'original sharedInstance au lieu de simpleSharedInstance. Je pense que c'est peut-être parce que ce sont deux méthodes au niveau de la classe ... Le remplacement que j'ai utilisé était: class_replaceMethod (self, origSel, method_getImplementation (newMethod), method_getTypeEncoding (newMethod)); et certaines de ses variantes. Je peux vérifier que le code que j'ai publié fonctionne et simpleSharedInstance est appelé après le premier passage via sharedInstance.
Kendall Helmstetter Gelner
Vous pouvez créer une version thread-safe qui ne paie pas les frais de verrouillage après l'initialisation sans faire un tas de déblayage d'exécution, j'ai posté une implémentation ci-dessous.
Louis Gerbarg
1
+1 bonne idée. J'adore ces choses que l'on peut faire avec le runtime. Mais dans la plupart des cas, il s'agit probablement d'une optimisation prématurée. Si je devais vraiment me débarrasser du coût de synchronisation, j'utiliserais probablement la version sans verrou de Louis.
Assurez-vous de lire l'en -tête dispatch / once.h pour comprendre ce qui se passe. Dans ce cas, les commentaires d'en-tête sont plus applicables que les documents ou la page de manuel.
La seule limitation concernant la classe Singleton, c'est qu'il s'agit de la sous-classe NSObject. Mais la plupart du temps, j'utilise des singletons dans mon code, ce sont en fait des sous-classes NSObject, donc cette classe me facilite vraiment la vie et rend le code plus propre.
J'utilise votre NSSingleton pour mon projet, et il semble être incompatible avec KVO. Le problème est que KVO crée une sous-classe pour chaque objet KVO avec le préfixe NSKVONotifying_ MyClass . Et cela fait que les méthodes MyClass + initialize et -init sont appelées deux fois.
Oleg Trakhman
J'ai testé cela sur le dernier Xcode et je n'ai eu aucun problème à m'inscrire ou à recevoir des événements KVO. Vous pouvez le vérifier avec le code suivant: gist.github.com/3065038 Comme je l'ai mentionné sur Twitter, les méthodes + initialize sont appelées une fois pour NSSingleton et une fois pour chaque sous-classe. Ceci est une propriété d'Objective-C.
kevinlawler
Si vous ajoutez NSLog(@"initialize: %@", NSStringFromClass([self class]));à la +initializeméthode, vous pouvez vérifier que les classes ne sont initialisées qu'une seule fois.
Vous ne voulez pas vous synchroniser sur vous-même ... Puisque le self-objet n'existe pas encore! Vous finissez par verrouiller une valeur d'ID temporaire. Vous voulez vous assurer que personne d'autre ne peut exécuter les méthodes de classe (sharedInstance, alloc, allocWithZone :, etc), vous devez donc vous synchroniser sur l'objet classe à la place:
@implementationMYSingletonstaticMYSingleton* sharedInstance = nil;+( id )sharedInstance {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[[MYSingleton alloc ] init ];}return sharedInstance;}+( id )allocWithZone:(NSZone*)zone {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[ super allocWithZone:zone ];}return sharedInstance;}-( id )init {@synchronized([MYSingletonclass]){
self =[ super init ];if( self != nil ){// Insert initialization code here}return self;}}@end
Les autres méthodes, méthodes d'accesseur, méthodes de mutation, etc. doivent se synchroniser sur elles-mêmes. Toutes les méthodes et initialiseurs de classe (+) (et probablement -dealloc) doivent se synchroniser sur l'objet classe. Vous pouvez éviter d'avoir à synchroniser manuellement si vous utilisez des propriétés Objective-C 2.0 au lieu des méthodes d'accesseur / mutateur. Tous les objets object.property et object.property = foo sont automatiquement synchronisés avec self.
Rob Dotson
3
Veuillez expliquer pourquoi vous pensez que l' selfobjet n'existe pas dans une méthode de classe. Le runtime détermine quelle implémentation de méthode appeler en fonction de la même valeur exacte qu'il fournit selfpour chaque méthode (classe ou instance).
dreamlax
2
À l'intérieur d'une méthode de classe, se selftrouve l'objet de classe. Essayez-le vous-même:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
jscs
0
Je voulais juste laisser ça ici pour ne pas le perdre. L'avantage de celui-ci est qu'il est utilisable dans InterfaceBuilder, ce qui est un énorme avantage. Ceci est tiré d'une autre question que j'ai posée :
Je sais qu'il y a beaucoup de commentaires sur cette "question", mais je ne vois pas beaucoup de gens suggérer d'utiliser une macro pour définir le singleton. C'est un modèle si commun et une macro simplifie considérablement le singleton.
Voici les macros que j'ai écrites sur la base de plusieurs implémentations Objc que j'ai vues.
Singeton.h
/**
@abstract Helps define the interface of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the implementation.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonInterface(TYPE, NAME) \
+(TYPE *)NAME;/**
@abstract Helps define the implementation of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the interface.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+(void)initialize \
{ \
static BOOL initialized = NO; \
if(!initialized) \
{ \
initialized = YES; \
__ ## NAME = [[TYPE alloc] init]; \} \
} \
\
\
+(TYPE *)NAME \
{ \
return __ ## NAME; \}
Pourquoi une macro d'interface quand elle est presque vide? Cohérence du code entre l'en-tête et les fichiers de code; maintenabilité au cas où vous voudriez ajouter des méthodes plus automatiques ou les changer.
J'utilise la méthode initialize pour créer le singleton tel qu'il est utilisé dans la réponse la plus populaire ici (au moment de la rédaction).
Avec les méthodes de classe Objective C, nous pouvons simplement éviter d'utiliser le modèle singleton de la manière habituelle, à partir de:
[[Librarian sharedInstance] openLibrary]
à:
[Librarian openLibrary]
en enveloppant la classe dans une autre classe qui a juste des méthodes de classe , de cette façon il n'y a aucune chance de créer accidentellement des instances en double, car nous ne créons aucune instance!
Si le singleton est déjà initialisé, le bloc LOCK ne sera pas entré. La deuxième vérification si (! Initialisé) est de s'assurer qu'il n'est pas encore initialisé lorsque le thread actuel acquiert le LOCK.
J'utilise généralement un code à peu près similaire à celui de la réponse de Ben Hoffstein (que j'ai également retiré de Wikipédia). Je l'utilise pour les raisons indiquées par Chris Hanson dans son commentaire.
Cependant, j'ai parfois besoin de placer un singleton dans une NIB, et dans ce cas, j'utilise ce qui suit:
Je laisse l'implémentation de -retain(etc.) au lecteur, bien que le code ci-dessus soit tout ce dont vous avez besoin dans un environnement de récupération de place.
Votre code n'est pas thread-safe. Il utilise synchronisé dans la méthode alloc, mais pas dans la méthode init. Vérifier le booléen initialisé n'est pas thread-safe.
Mecki
-5
La réponse acceptée, bien qu'elle compile, est incorrecte.
+(MySingleton*)sharedInstance
{@synchronized(self)<-------- self does not exist at class scope
{if(sharedInstance == nil)
sharedInstance =[[MySingleton alloc] init];}return sharedInstance;}
Selon la documentation Apple:
... Vous pouvez adopter une approche similaire pour synchroniser les méthodes de classe de la classe associée, en utilisant l'objet Class au lieu de self.
Même si l'utilisation d'auto fonctionne, cela ne devrait pas et cela ressemble à une erreur de copier-coller pour moi. L'implémentation correcte d'une méthode de fabrique de classe serait:
auto certainement ne fait exister ce champ de classe. Il fait référence à la classe au lieu de l'instance de la classe. Les classes sont (principalement) des objets de première classe.
schwa
Pourquoi mettez-vous @synchroninzed WITHIN dans une méthode?
user4951
1
Comme schwa l'a déjà dit, selfest l'objet de classe à l'intérieur d'une méthode de classe. Voir mon commentaire pour un extrait démontrant cela.
jscs
selfexiste, mais son utilisation comme identifiant transmis @synchronizedsynchronisera l'accès aux méthodes de l'instance. Comme le souligne @ user490696, il existe des cas (comme des singletons) où l'utilisation de l'objet classe est préférable. Du guide de programmation Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.
Réponses:
Une autre option consiste à utiliser la
+(void)initialize
méthode. De la documentation:Vous pourriez donc faire quelque chose de semblable à ceci:
la source
+initialize
leur super-classe, l'implémentation sera appelée si la sous-classe est d'abord utilisée.release
méthode et la rendre vide. :)[La source]
la source
MySingleton *s = [[MySingelton alloc] init];
Pro Objective-C Design Patterns for iOS
et il explique comment on fait un singelton "strict". Fondamentalement, puisque vous ne pouvez pas rendre les méthodes de lancement privées, vous devez remplacer les méthodes d'allocation et de copie. Donc, si vous essayez de faire quelque chose comme cela,[[MySingelton alloc] init]
vous obtiendrez une erreur d'exécution (mais pas une erreur de compilation malheureusement). Je ne comprends pas comment tous les détails de la création d'objet, mais vous implémentez+ (id) allocWithZone:(NSZone *)zone
ce qui s'appelle insharedSingleton
Selon mon autre réponse ci-dessous, je pense que vous devriez faire:
la source
Depuis que Kendall a publié un singleton threadsafe qui tente d'éviter les coûts de verrouillage, j'ai pensé que j'en jetterais un aussi:
D'accord, laissez-moi vous expliquer comment cela fonctionne:
Cas rapide: En exécution normale
sharedInstance
a déjà été définie, donc lawhile
boucle n'est jamais exécutée et la fonction revient après avoir simplement testé l'existence de la variable;Cas lent: S'il
sharedInstance
n'existe pas, alors une instance est allouée et copiée dedans à l'aide d'un Compare And Swap ('CAS');Cas envisagé: si deux threads tentent tous les deux d'appeler
sharedInstance
en même temps ETsharedInstance
n'existent pas en même temps, ils initialiseront tous les deux de nouvelles instances du singleton et tenteront de le mettre en position CAS. Celui qui gagne le CAS retourne immédiatement, celui qui perd libère l'instance qu'il vient d'allouer et retourne le (maintenant défini)sharedInstance
. Le singleOSAtomicCompareAndSwapPtrBarrier
agit à la fois comme barrière d'écriture pour le thread de configuration et comme barrière de lecture depuis le thread de test.la source
init
méthode dans votre approche? Jeter une exception quandsharedInstance
est initialisé n'est pas une bonne idée, je crois. Que faire alors pour empêcher l'utilisateur d'appelerinit
directement plusieurs fois?la source
[[self alloc] init]
à sharedInst.Edit: Cette implémentation obsolète avec ARC. Veuillez consulter Comment mettre en œuvre un singleton Objective-C compatible avec ARC? pour une mise en œuvre correcte.
Toutes les implémentations d'initialisation que j'ai lues dans d'autres réponses partagent une erreur commune.
La documentation Apple vous recommande de vérifier le type de classe dans votre bloc d'initialisation. Parce que les sous-classes appellent l'initialisation par défaut. Il existe un cas non évident où des sous-classes peuvent être créées indirectement via KVO. Car si vous ajoutez la ligne suivante dans une autre classe:
Objective-C créera implicitement une sous-classe de MySingletonClass entraînant un deuxième déclenchement de
+initialize
.Vous pouvez penser que vous devriez implicitement vérifier l'initialisation en double dans votre bloc init en tant que tel:
Mais vous vous tirerez une balle dans le pied; ou pire, donner à un autre développeur la possibilité de se tirer une balle dans le pied.
TL; DR, voici mon implémentation
(Remplacez ZAssert par notre propre macro d'assertion; ou simplement NSAssert.)
la source
Une explication approfondie du code macro Singleton est sur le blog Cocoa With Love
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .
la source
J'ai une variation intéressante sur sharedInstance qui est thread-safe, mais ne se verrouille pas après l'initialisation. Je n'en suis pas encore assez sûr pour modifier la première réponse comme demandé, mais je la présente pour une discussion plus approfondie:
la source
class_replaceMethod
pour me transformersharedInstance
en clone desimpleSharedInstance
. De cette façon, vous n'aurez plus jamais à vous soucier d'acquérir une@synchronized
serrure.Réponse courte: Fabuleux.
Réponse longue: Quelque chose comme ...
Assurez-vous de lire l'en -tête dispatch / once.h pour comprendre ce qui se passe. Dans ce cas, les commentaires d'en-tête sont plus applicables que les documents ou la page de manuel.
la source
J'ai roulé singleton dans une classe, afin que d'autres classes puissent hériter des propriétés singleton.
Singleton.h:
Singleton.m:
Et voici un exemple d'une classe, que vous voulez devenir singleton.
La seule limitation concernant la classe Singleton, c'est qu'il s'agit de la sous-classe NSObject. Mais la plupart du temps, j'utilise des singletons dans mon code, ce sont en fait des sous-classes NSObject, donc cette classe me facilite vraiment la vie et rend le code plus propre.
la source
@synchronized
est horriblement lent et doit être évité.Cela fonctionne également dans un environnement non récupéré.
la source
Cela ne devrait-il pas être threadsafe et éviter le verrouillage coûteux après le premier appel?
la source
Voici une macro que j'ai composée:
http://github.com/cjhanson/Objective-C-Optimized-Singleton
Il est basé sur le travail réalisé ici par Matt Gallagher mais en changeant l'implémentation pour utiliser la méthode swizzling comme décrit ici par Dave MacLachlan de Google .
J'accueille les commentaires / contributions.
la source
Que diriez-vous
Vous évitez ainsi le coût de synchronisation après l'initialisation?
la source
Pour une discussion approfondie du modèle singleton dans Objective-C, regardez ici:
Utilisation du modèle singleton dans Objective-C
la source
KLSingleton
la source
NSLog(@"initialize: %@", NSStringFromClass([self class]));
à la+initialize
méthode, vous pouvez vérifier que les classes ne sont initialisées qu'une seule fois.Vous ne voulez pas vous synchroniser sur vous-même ... Puisque le self-objet n'existe pas encore! Vous finissez par verrouiller une valeur d'ID temporaire. Vous voulez vous assurer que personne d'autre ne peut exécuter les méthodes de classe (sharedInstance, alloc, allocWithZone :, etc), vous devez donc vous synchroniser sur l'objet classe à la place:
la source
self
objet n'existe pas dans une méthode de classe. Le runtime détermine quelle implémentation de méthode appeler en fonction de la même valeur exacte qu'il fournitself
pour chaque méthode (classe ou instance).self
trouve l'objet de classe. Essayez-le vous-même:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
Je voulais juste laisser ça ici pour ne pas le perdre. L'avantage de celui-ci est qu'il est utilisable dans InterfaceBuilder, ce qui est un énorme avantage. Ceci est tiré d'une autre question que j'ai posée :
la source
la source
Je sais qu'il y a beaucoup de commentaires sur cette "question", mais je ne vois pas beaucoup de gens suggérer d'utiliser une macro pour définir le singleton. C'est un modèle si commun et une macro simplifie considérablement le singleton.
Voici les macros que j'ai écrites sur la base de plusieurs implémentations Objc que j'ai vues.
Singeton.h
Exemple d'utilisation:
MyManager.h
MyManager.m
Pourquoi une macro d'interface quand elle est presque vide? Cohérence du code entre l'en-tête et les fichiers de code; maintenabilité au cas où vous voudriez ajouter des méthodes plus automatiques ou les changer.
J'utilise la méthode initialize pour créer le singleton tel qu'il est utilisé dans la réponse la plus populaire ici (au moment de la rédaction).
la source
Avec les méthodes de classe Objective C, nous pouvons simplement éviter d'utiliser le modèle singleton de la manière habituelle, à partir de:
à:
en enveloppant la classe dans une autre classe qui a juste des méthodes de classe , de cette façon il n'y a aucune chance de créer accidentellement des instances en double, car nous ne créons aucune instance!
J'ai écrit un blog plus détaillé ici :)
la source
Pour étendre l'exemple de @ robbie-hanson ...
la source
Ma façon est simple comme ça:
Si le singleton est déjà initialisé, le bloc LOCK ne sera pas entré. La deuxième vérification si (! Initialisé) est de s'assurer qu'il n'est pas encore initialisé lorsque le thread actuel acquiert le LOCK.
la source
initialized
commevolatile
suffisant soit suffisant. Voir aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf .Je n'ai pas lu toutes les solutions, alors pardonnez-moi si ce code est redondant.
À mon avis, c'est la mise en œuvre la plus sûre pour les threads.
la source
J'utilise généralement un code à peu près similaire à celui de la réponse de Ben Hoffstein (que j'ai également retiré de Wikipédia). Je l'utilise pour les raisons indiquées par Chris Hanson dans son commentaire.
Cependant, j'ai parfois besoin de placer un singleton dans une NIB, et dans ce cas, j'utilise ce qui suit:
Je laisse l'implémentation de
-retain
(etc.) au lecteur, bien que le code ci-dessus soit tout ce dont vous avez besoin dans un environnement de récupération de place.la source
La réponse acceptée, bien qu'elle compile, est incorrecte.
Selon la documentation Apple:
... Vous pouvez adopter une approche similaire pour synchroniser les méthodes de classe de la classe associée, en utilisant l'objet Class au lieu de self.
Même si l'utilisation d'auto fonctionne, cela ne devrait pas et cela ressemble à une erreur de copier-coller pour moi. L'implémentation correcte d'une méthode de fabrique de classe serait:
la source
self
est l'objet de classe à l'intérieur d'une méthode de classe. Voir mon commentaire pour un extrait démontrant cela.self
existe, mais son utilisation comme identifiant transmis@synchronized
synchronisera l'accès aux méthodes de l'instance. Comme le souligne @ user490696, il existe des cas (comme des singletons) où l'utilisation de l'objet classe est préférable. Du guide de programmation Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.